* 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
--- 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)
--- /dev/null
+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);
--- /dev/null
+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;
===================================================================
--- 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>
+ * 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) {
+ /*
===================================================================
--- 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>
+ * 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) {
+ /*
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
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
default: all
-MODULES := ldiskfs quotafmt_test
+MODULES := ldiskfs #quotafmt_test
# copy makefile over to not break patches
ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile)
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
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':"
@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
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
- 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)
- 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
- 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)
-@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@
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
])
#
+# 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
# 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
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
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])
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)
])
#
-m4_define([LUSTRE_VERSION],[1.4.1.11])
+m4_define([LUSTRE_VERSION],[1.4.3.3])
#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>
#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.
/* 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)
{
#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)
/* 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 */
/* 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)
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)
}
#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)
#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; }
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>
struct obd_device;
struct file;
+struct obd_histogram;
#ifdef LPROCFS
__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);
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,
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
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];
#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)
#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)
#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)
#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)
{
#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 */
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);
#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
#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,
[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)
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;
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,
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 {
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;
__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 {
/* -*- 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.
*
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);
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);
};
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);
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,
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
//#define MSG_CONNECT_PEER 0x8
#define MSG_CONNECT_LIBCLIENT 0x10
#define MSG_CONNECT_INITIAL 0x20
+#define MSG_CONNECT_ASYNC 0x40
/* Connect flags */
#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)
{
REINT_UNLINK = 4,
REINT_RENAME = 5,
REINT_OPEN = 6,
+// REINT_CLOSE = 7,
+// REINT_WRITE = 8,
REINT_MAX
} mds_reint_t;
* 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
/* 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;
struct ldlm_extent {
__u64 start;
__u64 end;
+ __u64 gid;
};
struct ldlm_flock {
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,
/* 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);
# 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
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);
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;
}
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
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) {
data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset;
}
- EXIT;
- return 0;
+ RETURN(0);
}
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)
* 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;
#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 || \
#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
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
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 *);
/* ...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.
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;
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 */
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;
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)
{
#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, \
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 {
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 */
#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)
{
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)
{
{
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 */
#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 {
#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
#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__
#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 {
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
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
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;
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;
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);
#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;
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);
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 {
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 *);
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 */
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
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;
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);
}
{
int rc;
ENTRY;
-
- OBD_CHECK_DEV(obd);
+
+ OBD_CHECK_DEV(obd);
OBD_CHECK_OP(obd, cleanup, 0);
OBD_COUNTER_INCREMENT(obd, cleanup);
{
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);
}
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);
}
{
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);
}
}
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;
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;
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)
{
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)
{
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)
}
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;
rc = OBP(exp->exp_obd, quotacheck)(exp, oqctl);
RETURN(rc);
-}
+}
static inline int obd_quotactl(struct obd_export *exp,
struct obd_quotactl *oqctl)
rc = OBP(exp->exp_obd, quotactl)(exp, oqctl);
RETURN(rc);
-}
+}
static inline int obd_register_observer(struct obd_device *obd,
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;
#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
#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
#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
#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); \
} \
} 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
} 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");
#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)
{
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;
}
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)); \
(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__
(ptr) = (void *)0xdeadbeef; \
} while (0)
+#define KEY_IS(str) \
+ (keylen == strlen(str) && memcmp(key, str, keylen) == 0)
+
#endif
#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>
#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
};
#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
};
#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 {
# 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
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
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
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
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
#
# 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
# 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
# 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
#
# 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
# 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
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
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
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
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
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
# 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
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
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
# 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
# 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
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
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
# 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
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
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
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
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
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
# 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
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
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
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
# 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
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
# 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
# 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
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
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
#
# Bus options (PCI, PCMCIA)
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
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
#
# Bus options (PCI, PCMCIA)
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
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
#
# CPU Frequency scaling
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
CONFIG_ACPI_PCI=y
CONFIG_ACPI_SYSTEM=y
CONFIG_ACPI_INITRD=y
+CONFIG_IOPROC=y
+CONFIG_PTRACK=y
#
# CPU Frequency scaling
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
#
# 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
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
# 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
# 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
#
# 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
#
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
# 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
# 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
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
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
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
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
# 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
# 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
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
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
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
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
#
# CONFIG_PCMCIA_QLOGIC is not set
# CONFIG_PCMCIA_SYM53C500 is not set
-
#
# Old CD-ROM drivers (not SCSI, not IDE)
#
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
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
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
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
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_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
# 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
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
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
CONFIG_IP6_NF_TARGET_MARK=m
CONFIG_IP6_NF_RAW=m
-
#
# Bridge: Netfilter Configuration
#
# 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
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
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
# 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)
# 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
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
# 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
# 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
CONFIG_E100=m
CONFIG_E100_NAPI=y
CONFIG_FEALNX=m
-CONFIG_FORCEDETH=m
CONFIG_NATSEMI=m
CONFIG_NE2K_PCI=m
CONFIG_8139CP=m
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
CONFIG_R8169=m
CONFIG_R8169_NAPI=y
CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
CONFIG_TIGON3=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)
CONFIG_PCI_HERMES=m
CONFIG_ATMEL=m
CONFIG_PCI_ATMEL=m
-CONFIG_PRISM54=m
#
# Wireless 802.11b Pcmcia/Cardbus cards support
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
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
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
CONFIG_ISDN_CAPI_CAPI20=m
CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
CONFIG_ISDN_CAPI_CAPIFS=m
+CONFIG_ISDN_CAPI_CAPIDRV=m
#
# CAPI hardware drivers
# 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
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
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
# 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
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
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
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
#
#
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
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
#
# 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
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FONTS is not set
-
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
#
# Logo configuration
# 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
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
#
# 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
# 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
#
# 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
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
#
# 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
# 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
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
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)
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
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
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
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
# 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
CONFIG_ZISOFS=y
CONFIG_ZISOFS_FS=y
CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
#
# DOS/FAT/NT 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
#
# 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
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
# 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
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
CONFIG_NLS_KOI8_R=m
CONFIG_NLS_KOI8_U=m
CONFIG_NLS_UTF8=m
-CONFIG_NLS_ASCII=y
#
# Profiling support
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
# 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
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
#
# 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
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
# 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
# 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
#
# 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
#
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
# 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
# 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
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
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
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
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
# 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
# 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
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
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
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
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
# CONFIG_PCMCIA_QLOGIC is not set
# CONFIG_PCMCIA_SYM53C500 is not set
-
#
# Old CD-ROM drivers (not SCSI, not IDE)
#
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
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
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
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
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_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
# 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
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
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
CONFIG_IP6_NF_TARGET_MARK=m
CONFIG_IP6_NF_RAW=m
-
#
# Bridge: Netfilter Configuration
#
# 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
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
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
# 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)
# 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
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
# 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
# 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
CONFIG_E100=m
CONFIG_E100_NAPI=y
CONFIG_FEALNX=m
-CONFIG_FORCEDETH=m
CONFIG_NATSEMI=m
CONFIG_NE2K_PCI=m
CONFIG_8139CP=m
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
CONFIG_R8169=m
CONFIG_R8169_NAPI=y
CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
CONFIG_TIGON3=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)
CONFIG_PCI_HERMES=m
CONFIG_ATMEL=m
CONFIG_PCI_ATMEL=m
-CONFIG_PRISM54=m
#
# Wireless 802.11b Pcmcia/Cardbus cards support
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
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
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
CONFIG_ISDN_CAPI_CAPI20=m
CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
CONFIG_ISDN_CAPI_CAPIFS=m
+CONFIG_ISDN_CAPI_CAPIDRV=m
#
# CAPI hardware drivers
# 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
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
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
# 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
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
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
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
#
#
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
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
#
# 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
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FONTS is not set
-
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
#
# Logo configuration
# 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
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
#
# 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
# 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
#
# 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
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
#
# 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
# 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
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
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)
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
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
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
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
# 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
CONFIG_ZISOFS=y
CONFIG_ZISOFS_FS=y
CONFIG_UDF_FS=m
+CONFIG_UDF_NLS=y
#
# DOS/FAT/NT 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
#
# 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
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
# 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
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
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
# 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
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
--- /dev/null
+#
+# 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
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
#
---- ./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 */
/*
* 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
*/
/* 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;
}
*/
-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;
- 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) {
- 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)
- __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);
- 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);
+ /* 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;
+ }
/*
* 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 *
}
/*
+ * 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;
+
+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);
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)
{
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));
}
--- 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;
}
--- /dev/null
+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
--- /dev/null
+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>
{
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);
ret = bh;
goto cleanup_and_exit;
} else {
-@@ -198,6 +851,66 @@
+@@ -198,6 +851,74 @@
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)) {
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)) {
--- /dev/null
+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 */
--- /dev/null
+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
#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
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;
}
}
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");
#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
#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
--- /dev/null
+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 */
--- 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)
ret = bh;
goto cleanup_and_exit;
} else {
-@@ -198,6 +863,66 @@
+@@ -198,6 +863,74 @@
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)) {
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>
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)) {
--- /dev/null
+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,
--- /dev/null
+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);
--- /dev/null
+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);
--- /dev/null
+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;
--- /dev/null
+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;
===================================================================
--- 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>
+ * 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) {
+ /*
===================================================================
--- 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>
+ * 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) {
+ /*
--- /dev/null
+--- 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;
+ };
--- /dev/null
+--- 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;
--- /dev/null
+--- 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);
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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);
+ }
+
+ /*
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+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);
+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)
--- /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
_
--- 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);
+
--- /dev/null
+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 = ⁢
++
+ 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);
--- /dev/null
+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 = ⁢
++
+ 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);
--- /dev/null
+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:
--- /dev/null
+===== 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)
--- /dev/null
+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 (¤t->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 (¤t->mm->mmap_sem);
++ return EFAULT;
++ }
++
++ if (writeable && !(area->vm_flags & VM_WRITE))
++ {
++ PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p not writeable\n", mainAddr);
++ up_read (¤t->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 (¤t->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 (¤t->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 (¤t->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 (¤t->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 (¤t->mm->page_table_lock);
++ register_coproc_ops (current->mm, &pr->pr_coproc);
++ spin_unlock (¤t->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 (¤t->mm->page_table_lock);
++ if (pr->pr_mm != current->mm)
++ spin_unlock (¤t->mm->page_table_lock);
++ else
++ {
++ unregister_coproc_ops (current->mm, &pr->pr_coproc);
++ spin_unlock (¤t->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 (¤t->mm->mmap_sem);
++ ptr = do_mmap_pgoff (file, (unsigned long) maddr, av[1], av[2], av[3], av[5] >>PAGE_SHIFT);
++ up_write (¤t->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, ¶ms, &mask);
++
++ if (copy_to_user ((void *) arg, ¶ms, sizeof (ELAN_PARAMS)))
++ return (-EFAULT);
++
++ return (0);
++ }
++
++ case ELAN4IO_OLD_SET_PARAMS:
++ {
++ ELAN_PARAMS params;
++
++ if (copy_from_user (¶ms, (void *) arg, sizeof (ELAN_PARAMS)))
++ return (-EFAULT);
++
++ elan4_set_params (pr->pr_dev, ¶ms, 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 (¤t->mm->mm_users), atomic_read (¤t->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 (¤t->mm->page_table_lock);
++ register_coproc_ops (current->mm, &pr->pr_coproc);
++ spin_unlock (¤t->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 (¤t->mm->page_table_lock);
++ if (pr->pr_mm != current->mm)
++ spin_unlock (¤t->mm->page_table_lock);
++ else
++ {
++ unregister_coproc_ops (current->mm, &pr->pr_coproc);
++ spin_unlock (¤t->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_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_link);
++ list_add_tail (¢->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_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 (¤t->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 (¤t->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(¤t_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(¤t_kmem);
++ break;
++
++ case 'c' :
++ print_kmem(¤t_kmem);
++ break;
++
++ case 'o' :
++ print_kmem(&stored_kmem);
++ break;
++
++ case 's' :
++ clear_kmem(&stored_kmem);
++ move_kmem(&stored_kmem, ¤t_kmem);
++ break;
++
++ case 'd' :
++ diff_kmem(¤t_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(¤t->sigmask_lock);
++ flush_signals(current);
++ sigfillset(¤t->blocked);
++
++#ifdef NO_NPTL
++ recalc_sigpending(current);
++#else
++ recalc_sigpending();
++#endif
++
++ spin_unlock_irq(¤t->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, ¤t->ptrack_list);
++
++ return 0;
++}
++
++void
++ptrack_deregister (ptrack_callback_t callback, void *arg)
++{
++ struct list_head *el, *nel;
++
++ list_for_each_safe (el, nel, ¤t->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, ¤t->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, ¤t->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(¤t->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);
--- /dev/null
+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 (¤t->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 (¤t->mm->mmap_sem);
++ return EFAULT;
++ }
++
++ if (writeable && !(area->vm_flags & VM_WRITE))
++ {
++ PRINTF (ctxt, DBG_FAULT, "LoadElanTranslation: %p not writeable\n", mainAddr);
++ up_read (¤t->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 (¤t->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 (¤t->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 (¤t->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 (¤t->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 (¤t->mm->page_table_lock);
++ ioproc_register_ops (current->mm, &pr->pr_ioproc);
++ spin_unlock (¤t->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 (¤t->mm->page_table_lock);
++ if (pr->pr_mm != current->mm)
++ spin_unlock (¤t->mm->page_table_lock);
++ else
++ {
++ ioproc_unregister_ops (current->mm, &pr->pr_ioproc);
++ spin_unlock (¤t->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 (¤t->mm->mmap_sem);
++ ptr = do_mmap_pgoff (file, (unsigned long) maddr, av[1], av[2], av[3], av[5] >>PAGE_SHIFT);
++ up_write (¤t->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, ¶ms, &mask);
++
++ if (copy_to_user ((void *) arg, ¶ms, sizeof (ELAN_PARAMS)))
++ return (-EFAULT);
++
++ return (0);
++ }
++
++ case ELAN4IO_OLD_SET_PARAMS:
++ {
++ ELAN_PARAMS params;
++
++ if (copy_from_user (¶ms, (void *) arg, sizeof (ELAN_PARAMS)))
++ return (-EFAULT);
++
++ elan4_set_params (pr->pr_dev, ¶ms, 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 (¤t->mm->mm_users), atomic_read (¤t->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 (¤t->mm->page_table_lock);
++ ioproc_register_ops (current->mm, &pr->pr_ioproc);
++ spin_unlock (¤t->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 (¤t->mm->page_table_lock);
++ if (pr->pr_mm != current->mm)
++ spin_unlock (¤t->mm->page_table_lock);
++ else
++ {
++ ioproc_unregister_ops (current->mm, &pr->pr_ioproc);
++ spin_unlock (¤t->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_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_link);
++ list_add_tail (¢->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_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 (¤t->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 (¤t->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(¤t_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(¤t_kmem);
++ break;
++
++ case 'c' :
++ print_kmem(¤t_kmem);
++ break;
++
++ case 'o' :
++ print_kmem(&stored_kmem);
++ break;
++
++ case 's' :
++ clear_kmem(&stored_kmem);
++ move_kmem(&stored_kmem, ¤t_kmem);
++ break;
++
++ case 'd' :
++ diff_kmem(¤t_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(¤t->sigmask_lock);
++ flush_signals(current);
++ sigfillset(¤t->blocked);
++
++#ifdef NO_NPTL
++ recalc_sigpending(current);
++#else
++ recalc_sigpending();
++#endif
++
++ spin_unlock_irq(¤t->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, ¤t->ptrack_list);
++
++ return 0;
++}
++
++void
++ptrack_deregister (ptrack_callback_t callback, void *arg)
++{
++ struct list_head *el, *nel;
++
++ list_for_each_safe (el, nel, ¤t->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, ¤t->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, ¤t->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(¤t->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: 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;
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;
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;
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"
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));
#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;
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;
} 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++) {
}
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++)
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) {
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;
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++) {
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++) {
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 )
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 )
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;
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;
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
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",
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++)
{
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;
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;
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;
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;
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;
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--)
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);
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));
}
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;
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) {
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;
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
{
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));
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;
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;
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;
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 *)
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 ) {
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;
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;
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;
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;
}
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;
}
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++;
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++;
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
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) {
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) {
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>
#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;
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;
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)
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)
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))
}
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++) {
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;
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;
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;
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++;
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,
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]);
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;
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) {
/* 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;
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++) {
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;
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;
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) {
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,
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) {
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) {
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 @@
}
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>
#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 *)
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"
}
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
*/
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
#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)
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];
--- /dev/null
+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;
+++ /dev/null
-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
--- /dev/null
+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);
+ 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){
--- /dev/null
+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", ¬_configged_ops },
++#endif
++
++#ifdef CONFIG_NULL_CHAN
++ { "null", &null_ops },
++#else
++ { "null", ¬_configged_ops },
++#endif
++
++#ifdef CONFIG_PORT_CHAN
++ { "port", &port_ops },
++#else
++ { "port", ¬_configged_ops },
++#endif
++
++#ifdef CONFIG_PTY_CHAN
++ { "pty", &pty_ops },
++ { "pts", &pts_ops },
++#else
++ { "pty", ¬_configged_ops },
++ { "pts", ¬_configged_ops },
++#endif
++
++#ifdef CONFIG_TTY_CHAN
++ { "tty", &tty_ops },
++#else
++ { "tty", ¬_configged_ops },
++#endif
++
++#ifdef CONFIG_XTERM_CHAN
++ { "xterm", &xterm_ops },
++#else
++ { "xterm", ¬_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(¤t->sigmask_lock);
++ sigfillset(¤t->blocked);
++ flush_signals(current);
++ spin_unlock_irq(¤t->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, ¤t->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(¬ify_spinlock);
++}
++
++void unlock_notify(void)
++{
++ spin_unlock(¬ify_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(ðcmd, 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, ð_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(ð->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, ð_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, ð_cmd_line){
++ eth = list_entry(ele, struct eth_init, list);
++
++ if(eth_setup_common(eth->init, eth->index))
++ list_del(ð->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(¨_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, §orsize, &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(®s->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(®s), \
++ UM_SYSCALL_ARG2(®s), \
++ UM_SYSCALL_ARG3(®s), \
++ UM_SYSCALL_ARG4(®s), \
++ UM_SYSCALL_ARG5(®s), \
++ UM_SYSCALL_ARG6(®s))
++
++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, ¤t->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) ≻
++ 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, ¤t->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),
++ ¤t->thread.fault_addr,
++ ¤t->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(¤t->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(¤t->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 = ¤t_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 = ¤t_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 = ¤t->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(¤t->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(¤t->sigmask_lock);
++ sigorsets(¤t->blocked, ¤t->blocked,
++ &ka->sa.sa_mask);
++ sigaddset(¤t->blocked, signr);
++ recalc_sigpending(current);
++ spin_unlock_irq(¤t->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 = ¤t->blocked;
++
++ for (;;) {
++ unsigned long signr;
++
++ spin_lock_irq(¤t->sigmask_lock);
++ signr = dequeue_signal(¤t->blocked, &info);
++ spin_unlock_irq(¤t->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(¤t->blocked, signr)) {
++ send_sig_info(signr, &info, current);
++ continue;
++ }
++ }
++
++ ka = ¤t->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, ¤t->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(¤t->thread.regs)))
++ current->thread.singlestep_syscall = 1;
++
++ return(0);
++}
++
++int do_signal(int error)
++{
++ return(kern_do_signal(¤t->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(¤t->sigmask_lock);
++ saveset = current->blocked;
++ siginitset(¤t->blocked, mask);
++ recalc_sigpending(current);
++ spin_unlock_irq(¤t->sigmask_lock);
++
++ PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
++ while (1) {
++ current->state = TASK_INTERRUPTIBLE;
++ schedule();
++ if(kern_do_signal(¤t->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(¤t->sigmask_lock);
++ saveset = current->blocked;
++ current->blocked = newset;
++ recalc_sigpending(current);
++ spin_unlock_irq(¤t->sigmask_lock);
++
++ PT_REGS_SYSCALL_RET(¤t->thread.regs) = -EINTR;
++ while (1) {
++ current->state = TASK_INTERRUPTIBLE;
++ schedule();
++ if (kern_do_signal(¤t->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(¤t->thread.regs));
++ void *mask = sp_to_mask(PT_REGS_SP(¤t->thread.regs));
++ int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
++
++ spin_lock_irq(¤t->sigmask_lock);
++ copy_from_user(¤t->blocked.sig[0], sc_sigmask(sc),
++ sizeof(current->blocked.sig[0]));
++ copy_from_user(¤t->blocked.sig[1], mask, sig_size);
++ sigdelsetmask(¤t->blocked, ~_BLOCKABLE);
++ recalc_sigpending(current);
++ spin_unlock_irq(¤t->sigmask_lock);
++ copy_sc_from_user(¤t->thread.regs, sc,
++ &signal_frame_sc.common.arch);
++ return(PT_REGS_SYSCALL_RET(¤t->thread.regs));
++}
++
++int sys_rt_sigreturn(struct pt_regs regs)
++{
++ struct ucontext *uc = sp_to_uc(PT_REGS_SP(¤t->thread.regs));
++ int sig_size = _NSIG_WORDS * sizeof(unsigned long);
++
++ spin_lock_irq(¤t->sigmask_lock);
++ copy_from_user(¤t->blocked, &uc->uc_sigmask, sig_size);
++ sigdelsetmask(¤t->blocked, ~_BLOCKABLE);
++ recalc_sigpending(current);
++ spin_unlock_irq(¤t->sigmask_lock);
++ copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext,
++ &signal_frame_si.common.arch);
++ return(PT_REGS_SYSCALL_RET(¤t->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(¤t->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, ¤t->thread.exec_buf);
++ if(n == 1)
++ userspace(¤t->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(¤t->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(¤t->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,
++ ¤t->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, ©, 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(®s->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(¤t->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(®s.regs) = (struct sigcontext *) (&sig + 1)),
++ (void) (regs.regs.skas.is_user = 0));
++ do_timer(®s);
++}
++
++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(¤t->thread.regs.regs) = (void *) (&sig + 1);
++ disable = (1 << (SIGVTALRM - 1)) | (1 << (SIGALRM - 1)) |
++ (1 << (SIGIO - 1)) | (1 << (SIGPROF - 1));
++ SC_SIGMASK(UPT_SC(¤t->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, ¤t->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(¤t->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(¤t->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(®s->regs),
++ UPT_SYSCALL_ARG2(®s->regs)));
++}
++
++static int check_utime(struct pt_regs *regs)
++{
++ return(check_area((void *) UPT_SYSCALL_ARG1(®s->regs),
++ sizeof(struct utimbuf)));
++}
++
++static int check_oldstat(struct pt_regs *regs)
++{
++ return(check_area((void *) UPT_SYSCALL_ARG1(®s->regs),
++ sizeof(struct __old_kernel_stat)));
++}
++
++static int check_stat(struct pt_regs *regs)
++{
++ return(check_area((void *) UPT_SYSCALL_ARG1(®s->regs),
++ sizeof(struct stat)));
++}
++
++static int check_stat64(struct pt_regs *regs)
++{
++ return(check_area((void *) UPT_SYSCALL_ARG1(®s->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(®s->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(®s->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, ¤t->thread.fault_addr,
++ ¤t->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, ¤t->thread.fault_addr,
++ ¤t->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,
++ ¤t->thread.fault_addr,
++ ¤t->thread.fault_catcher);
++ if(n < 0) return(-EFAULT);
++ return(n);
++}
++
++int __clear_user_tt(void *mem, int len)
++{
++ return(__do_clear_user(mem, len,
++ ¤t->thread.fault_addr,
++ ¤t->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, ¤t->thread.fault_addr,
++ ¤t->thread.fault_catcher));
++}
++
++int strnlen_user_tt(const void *str, int len)
++{
++ return(__do_strnlen_user(str, len,
++ ¤t->thread.fault_addr,
++ ¤t->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, ¤t_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', ¤t->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 = ðertap_user_info,
++ .kern = ðertap_kern_info,
++ .private_size = sizeof(struct ethertap_data),
++};
++
++static int register_ethertap(void)
++{
++ register_transport(ðertap_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 *) ®s);
++}
+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(®s->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(¤t->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(¤t->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, ¤t->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: 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;
#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;
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;
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) {
if (!IS_ERR(file)) {
err = deny_write_access(file);
if (err) {
-@@ -426,6 +432,7 @@
+@@ -430,6 +436,7 @@
return file;
}
}
path_release(&nd);
}
goto out;
-@@ -1355,7 +1362,7 @@
+@@ -1368,7 +1375,7 @@
goto close_fail;
if (!file->f_op->write)
goto close_fail;
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...
*/
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;
break;
continue;
/* here ends the main loop */
-@@ -590,12 +626,12 @@
+@@ -592,12 +628,12 @@
if (err < 0)
break;
}
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;
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;
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;
}
}
/* SMP-safe */
-@@ -749,6 +827,17 @@
+@@ -753,6 +833,17 @@
}
/* SMP-safe */
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;
if (*name=='/')
return walk_init_root(name,nd);
read_lock(¤t->fs->lock);
-@@ -777,7 +867,8 @@
+@@ -781,7 +873,8 @@
* needs parent already locked. Doesn't follow mounts.
* SMP-safe.
*/
{
struct dentry * dentry;
struct inode *inode;
-@@ -800,13 +891,16 @@
+@@ -804,13 +897,16 @@
goto out;
}
dentry = inode->i_op->lookup(inode, new);
unlock_kernel();
if (!dentry)
-@@ -818,6 +912,12 @@
+@@ -822,6 +918,12 @@
return dentry;
}
/* 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);
access:
return ERR_PTR(-EACCES);
}
-@@ -870,6 +970,23 @@
+@@ -874,6 +976,23 @@
return err;
}
/*
* 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;
}
{
int error;
-@@ -980,12 +1098,15 @@
+@@ -984,12 +1104,15 @@
goto exit_lock;
error = -EACCES; /* shouldn't it be ENOSYS? */
unlock_kernel();
exit_lock:
up(&dir->i_zombie);
-@@ -994,6 +1115,11 @@
+@@ -998,6 +1121,11 @@
return error;
}
/*
* open_namei()
*
-@@ -1008,7 +1134,8 @@
+@@ -1012,7 +1140,8 @@
* for symlinks (where the permissions are checked later).
* SMP-safe
*/
{
int acc_mode, error = 0;
struct inode *inode;
-@@ -1018,11 +1145,14 @@
+@@ -1023,11 +1152,14 @@
acc_mode = ACC_MODE(flag);
if (error)
return error;
dentry = nd->dentry;
-@@ -1032,6 +1162,10 @@
+@@ -1037,6 +1169,10 @@
/*
* Create - we need to know the parent.
*/
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);
do_last:
error = PTR_ERR(dentry);
-@@ -1056,11 +1190,12 @@
+@@ -1061,11 +1197,12 @@
goto exit;
}
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);
}
put_write_access(inode);
if (error)
-@@ -1176,8 +1311,10 @@
+@@ -1181,8 +1318,10 @@
return 0;
exit_dput:
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);
{
struct dentry *dentry;
-@@ -1232,7 +1379,7 @@
+@@ -1239,7 +1388,7 @@
dentry = ERR_PTR(-EEXIST);
if (nd->last_type != LAST_NORM)
goto fail;
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;
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);
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;
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);
path_release(&nd);
out:
putname(tmp);
-@@ -1466,8 +1639,16 @@
+@@ -1473,8 +1648,16 @@
error = -EBUSY;
goto exit1;
}
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;
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;
putname(to);
}
putname(from);
-@@ -1677,7 +1877,18 @@
+@@ -1684,7 +1886,18 @@
error = -EXDEV;
if (old_nd.mnt != nd.mnt)
goto out_release;
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,
{
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,
{
int error;
-@@ -1888,9 +2099,18 @@
+@@ -1895,9 +2108,18 @@
if (newnd.last_type != LAST_NORM)
goto exit2;
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;
}
dput(new_dentry);
exit4:
dput(old_dentry);
-@@ -1966,20 +2186,26 @@
+@@ -1973,20 +2195,26 @@
}
static inline int
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)
{
}
/* get the link contents into pagecache */
-@@ -2045,7 +2277,7 @@
+@@ -2052,7 +2286,7 @@
{
struct page *page = NULL;
char *s = page_getlink(dentry, &page);
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;
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>
int vfs_statfs(struct super_block *sb, struct statfs *buf)
{
-@@ -95,9 +97,10 @@
+@@ -169,9 +171,10 @@
write_unlock(&files->file_lock);
}
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;
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;
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);
path_release(&nd);
out:
return error;
-@@ -217,7 +229,7 @@
+@@ -291,7 +303,7 @@
error = locks_verify_truncate(inode, file, length);
if (!error)
out_putf:
fput(file);
out:
-@@ -262,11 +274,13 @@
+@@ -336,11 +348,13 @@
struct inode * inode;
struct iattr newattrs;
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;
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;
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;
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;
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;
path_release(&nd);
}
-@@ -387,8 +432,9 @@
+@@ -461,8 +506,9 @@
{
int error;
struct nameidata nd;
if (error)
goto out;
-@@ -399,6 +445,7 @@
+@@ -473,6 +519,7 @@
set_fs_pwd(current->fs, nd.mnt, nd.dentry);
dput_and_out:
path_release(&nd);
out:
return error;
-@@ -438,9 +485,10 @@
+@@ -512,9 +559,10 @@
{
int error;
struct nameidata nd;
if (error)
goto out;
-@@ -456,39 +504,56 @@
+@@ -530,39 +578,56 @@
set_fs_altroot();
error = 0;
dput_and_out:
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;
- 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;
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;
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;
{
struct file * f;
struct inode *inode;
-@@ -695,7 +760,9 @@
+@@ -769,7 +834,9 @@
}
if (f->f_op && f->f_op->open) {
if (error)
goto cleanup_all;
}
-@@ -711,6 +778,7 @@
+@@ -785,6 +852,7 @@
}
}
return f;
cleanup_all:
-@@ -725,11 +793,17 @@
+@@ -799,11 +867,17 @@
cleanup_file:
put_filp(f);
cleanup_dentry:
*/
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.
*/
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>
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
#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
/*
* 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;
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;
/* 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;
};
/*
-@@ -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 *);
/*
* File types
-@@ -928,21 +936,32 @@
+@@ -935,21 +943,32 @@
struct inode_operations {
int (*create) (struct inode *,struct dentry *,int);
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 */
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 *));
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)
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 *);
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;
}
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 */
}
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);
} 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);
--- /dev/null
+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(¤t->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(¤t->namespace->sem);
+ err = -EINVAL;
+@@ -518,6 +523,7 @@
+ }
+
+ up_write(¤t->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(¤t->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);
--- /dev/null
+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;
+
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
remove-suid-2.6-suse.patch
export-show_task-2.6-vanilla.patch
sd_iostats-2.6-rhel4.patch
+fsprivate-2.6.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
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
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
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
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
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
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
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
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
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
--- /dev/null
+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
--- /dev/null
+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
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
EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
RHBUILD=1
LINUX26=1
+LUSTRE_VERSION=@VERSION@
BASE_ARCHS="i686 x86_64 ia64"
BIGMEM_ARCHS=""
SERIES=2.6-suse-lnxi.series
VERSION=$lnxmaj
EXTRA_VERSION="${lnxrel}_lustre.@VERSION@"
+LUSTRE_VERSION=@VERSION@
RHBUILD=0
LINUX26=1
SUSEBUILD=1
SERIES=2.6-vanilla
VERSION=$lnxmaj
EXTRA_VERSION=lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
RHBUILD=0
BASE_ARCHS=""
SERIES=hp-pnnl-2.4.20
VERSION=$lnxmaj
EXTRA_VERSION=$lnxrel_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
RHBUILD=0
BASE_ARCHS="ia64"
SERIES=rh-2.4.20
VERSION=$lnxmaj
EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
RHBUILD=1
BASE_ARCHS="i686"
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
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
SERIES=suse-2.4.21-jvn
VERSION=${lnxmaj}
EXTRA_VERSION=${lnxrel}_lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
RHBUILD=0
LINUX26=0
SUSEBUILD=1
SERIES=suse-2.4.21-2
VERSION=2.4.21
EXTRA_VERSION=lustre.@VERSION@
+LUSTRE_VERSION=@VERSION@
RHBUILD=0
BASE_ARCHS="x86_64"
default: all
-MODULES := ldiskfs quotafmt_test
+MODULES := ldiskfs #quotafmt_test
# copy makefile over to not break patches
ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile)
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
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':"
@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
-/*
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
* Lustre administrative quota format.
*
* from
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);
}
/* 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);
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;
}
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;
}
}
/* Delete dquot from tree */
+#ifndef QFMT_NO_DELETE
static int lustre_delete_dquot(struct lustre_dquot *dquot)
{
uint tmp = LUSTRE_DQTREEOFF;
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)
/* 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);
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);
/* 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)) {
-/* 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);
/* 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)) {
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;
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;
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",
up(&parent_inode->i_sem);
}
- pop_ctxt(saved, &tgt->obd_ctxt, NULL);
+ pop_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL);
RETURN(rc);
}
{
int i;
ENTRY;
-
+
for (i = 0; i < MAXQUOTAS; i++) {
if (!lustre_check_quota_file(lqi, i))
RETURN(-EINVAL);
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
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);
CERROR("read quotainfo(%d) failed! (rc:%d)\n", i, rc);
break;
}
-
+
if(memcmp(&dqinfo, &lqi->qi_info[i], sizeof(dqinfo))) {
rc = -EINVAL;
break;
dquot->dq_dqb.dqb_curinodes = rand / 3;
dquot->dq_dqb.dqb_btime = jiffies;
dquot->dq_dqb.dqb_itime = jiffies;
-
+
return dquot;
}
struct mem_dqblk dqblk;
int rc = 0;
ENTRY;
-
+
dquot = get_rand_dquot(lqi);
if (dquot == NULL)
RETURN(-ENOMEM);
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) {
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;
/* 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);
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;
}
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)
.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("fmt_obd_ops, lvars.module_vars,
+ return class_register_type("fmt_obd_ops, lvars.module_vars,
"quotfmt_test");
}
# 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
/* 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;
__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);
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);
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);
}
{
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);
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
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
#include <libcfs/list.h>
#else
#include <liblustre.h>
+#include <linux/obd_class.h>
#endif
#include "ldlm_internal.h"
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);
/* 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);
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;
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);
((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);
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)
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);
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,
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
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);
}
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 &&
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);
}
}
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);
}
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);
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;
/* 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;
{
struct list_head *tmp, *n;
struct ptlrpc_request *req;
+ ENTRY;
LASSERT(obd->obd_stopping);
list_del(&req->rq_list);
target_release_saved_req(req);
}
+ EXIT;
}
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);
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;
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);
[LCK_PR] "PR",
[LCK_CW] "CW",
[LCK_CR] "CR",
- [LCK_NL] "NL"
+ [LCK_NL] "NL",
+ [LCK_GROUP] "GROUP"
};
char *ldlm_typename[] = {
[LDLM_PLAIN] "PLN",
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");
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);
OBD_SLAB_FREE(lock, ldlm_lock_slab, sizeof(*lock));
l_unlock(&ns->ns_lock);
+ if (export)
+ class_export_put(export);
}
EXIT;
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);
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);
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);
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--;
}
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;
(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 {
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);
} 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;
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);
/* 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);
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);
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);
}
{
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 "
" (%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,
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",
(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;
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) {
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,
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);
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) {
}
up(&lock->l_resource->lr_lvb_sem);
} else {
+ ldlm_resource_unlink_lock(lock);
ldlm_lock_destroy(lock);
}
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);
if (ldlm_state == NULL)
RETURN(-ENOMEM);
-#ifdef __KERNEL__
+#ifdef LPROCFS
rc = ldlm_proc_setup();
if (rc != 0)
GOTO(out_free, rc);
#endif
out_proc:
-#ifdef __KERNEL__
+#ifdef LPROCFS
ldlm_proc_cleanup();
out_free:
#endif
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));
"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);
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))) {
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;
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,
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");
}
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;
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);
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;
/* 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);
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;
}
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;
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);
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)
{
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)
{
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:
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));
}
/* 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);
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);
}
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);
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);
## 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 \
$(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
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
*
* 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;
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) {
}
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,
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;
}
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))
_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;
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);
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);
}
*
* 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
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)
/* already opened? */
if (lli->lli_open_count++)
RETURN(0);
-
+
LASSERT(!lli->lli_file_data);
OBD_ALLOC(fd, sizeof(*fd));
{
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;
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) {
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;
}
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;
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);
}
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;
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;
//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);
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))
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:
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;
}
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 |
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);
#
# 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
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=
#
# 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
# 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
# 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
*
* 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(¤t->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() ||
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;
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);
}
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);
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);
}
out:
if (rc == 0)
rc = err;
-
- RETURN(rc);
-}
-static void sighandler_USR1(int signum)
-{
- /* do nothing */
+ RETURN(rc);
}
/* parse host:/mdsname/profile string */
if ((s = strchr(buf, ':'))) {
*mdsnid = buf;
*s = '\0';
-
+
while (*++s == '/')
;
*mdsname = s;
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
}
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
};
#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
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;
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)
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;
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)
\
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 */
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;
}
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;
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 */
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;
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,
#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;
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;
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)
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();
static void init_capability(int *res)
{
+#ifdef HAVE_LIBCAP
cap_t syscap;
cap_flag_value_t capval;
int i;
}
}
}
+#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;
CERROR("Not enough memory\n");
return -ENOMEM;
}
- current->fs = ¤t->__fs;
- current->fs->umask = umask(0777);
- umask(current->fs->umask);
strncpy(current->comm, comm, sizeof(current->comm));
current->pid = getpid();
{
ptlrpc_exit_portals();
}
-
-int
-libcfs_nal_cmd(struct portals_cfg *pcfg)
-{
- /* handle portals command if we want */
- return 0;
-}
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);
*
* 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
EXIT;
}
+#if 0
/*
* remove the stale inode from pnode
*/
EXIT;
return;
}
+#endif
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);
}
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);
}
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
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));
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);
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);
}
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);
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);
}
/* 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;
int rc;
ENTRY;
+ liblustre_wait_event(0);
+
*inop = NULL;
/* the mount root inode have no name, so don't call
RETURN(rc);
}
-
*
* 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;
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:
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);
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;
(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,
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)
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;
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;
{
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;
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)
/* 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);
}
*
* 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;
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) {
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)
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)
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) {
{
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)
/* 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));
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]);
static int llu_inode_revalidate(struct inode *inode)
{
- struct llu_inode_info *lli = llu_i2info(inode);
struct lov_stripe_md *lsm = NULL;
ENTRY;
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);
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,
int rc;
ENTRY;
+ liblustre_wait_event(0);
+
if (!ino) {
LASSERT(pno);
LASSERT(pno->p_base->pb_ino);
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);
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;
}
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));
struct llu_inode_info *lli = llu_i2info(inode);
ENTRY;
+ liblustre_wait_event(0);
llu_clear_inode(inode);
OBD_FREE(lli, sizeof(*lli));
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) {
}
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:
{
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)) {
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);
}
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);
}
}
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? */
} 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 |
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) {
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));
}
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);
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;
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);
}
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);
}
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);
}
LASSERT(symname);
strncpy(data, symname, bufsize);
+ rc = strlen(symname);
ptlrpc_req_finished(request);
out:
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:
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;
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;
&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);
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);
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);
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;
}
*/
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;
}
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;
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;
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)
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");
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 */);
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) {
/* 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);
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);
}
ptlrpc_req_finished(request);
- printf("LibLustre: namespace mounted successfully!\n");
+ CDEBUG(D_SUPER, "LibLustre: %s mounted successfully!\n", source);
return 0;
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;
}
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,
#endif
inop_gone: llu_iop_gone,
};
-
-#warning "time_after() defined in liblustre.h need to be rewrite in userspace"
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
-#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;
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";
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() ||
#include "test_common.h"
+#define MAX_STRING_SIZE 2048
+
static struct {
const char *name;
unsigned long code;
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)
{
#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); \
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)
static struct option long_opts[] = {
{"target", 1, 0, 0},
{"dumpfile", 1, 0, 0},
+ {"ssh", 1, 0, 0},
{0, 0, 0, 0}
};
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]);
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_();
t2();
t3();
t4();
+#if 0
t5();
+#endif
t6();
t7();
#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()
{
void t0()
{
+ char *path="/mnt/lustre/f0";
ENTRY("empty replay");
replay_barrier();
mds_failover();
+ t_check_stat_fail("/mnt/lustre/f0");
LEAVE();
}
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);
}
}
static struct option long_opts[] = {
{"target", 1, 0, 0},
{"dumpfile", 1, 0, 0},
+ {"ssh", 1, 0, 0},
{0, 0, 0, 0}
};
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]);
!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);
#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);
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);
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);
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);
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();
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);
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);
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);
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++) {
}
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");
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);
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);
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);
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++) {
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);
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);
__liblustre_setup_();
-#ifndef __CYGWIN__
t1();
t2();
t3();
t4();
- t5();
t6();
+ t6b();
t7();
t8();
t9();
t13();
t14();
t15();
-#endif
+ t16();
+ t17();
+ t18();
+ t18b();
+ t19();
+ t20();
+ t21();
+ t22();
+ t50();
printf("liblustre is about shutdown\n");
__liblustre_cleanup_();
+/* -*- 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>
#include <string.h>
#include <errno.h>
#include <dirent.h>
+#include <utime.h>
+#include <stdarg.h>
#include "test_common.h"
{
int rc;
- rc = mkdir(path, 00644);
+ rc = mkdir(path, 00755);
if (rc < 0) {
printf("mkdir(%s) error: %s\n", path, strerror(errno));
EXIT(1);
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;
struct stat stat;
int rc;
+ memset(&stat, 0, sizeof(stat));
+
rc = lstat(name, &stat);
if (rc) {
printf("error %d stat %s\n", rc, name);
}
if (buf)
memcpy(buf, &stat, sizeof(*buf));
+ if (stat.st_blksize == 0) {
+ printf("error: blksize is 0\n");
+ EXIT_RET(-EINVAL);
+ }
return 0;
}
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);
+}
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);
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);
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
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@
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) {
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;
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);
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);
char *buf = NULL;
char *filename;
int namelen, rc, len = 0;
- unsigned long valid;
rc = obd_ioctl_getdata(&buf, &len, (void *)arg);
if (rc)
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);
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) {
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));
}
}
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 |
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);
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)
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
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;
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
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;
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)
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;
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);
/* 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);
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);
/* 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);
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);
}
(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);
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.
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);
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);
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)) {
#define LLITE_INTERNAL_H
#include <linux/lustre_debug.h>
+#include <linux/lustre_version.h>
/*
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))
#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;
#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))
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,
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
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 */
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))
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 {
#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;
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))
/* 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);
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);
}
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);
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");
/* 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) {
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) {
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);
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);
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;
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);
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);
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)
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));
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) {
LBUG();
return NULL;
}
-
#include <linux/iobuf.h>
#endif
-
#define DEBUG_SUBSYSTEM S_LLITE
#include <linux/lustre_mds.h>
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);
#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;
#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,
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 },
{ "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 }
};
[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",
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;
}
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);
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);
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.
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;
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;
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)
{
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);
/* 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;
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;
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;
while (--total >= 0 && count < want) {
struct page *page;
+ int keep;
if (unlikely(need_resched())) {
spin_unlock(&sbi->ll_lock);
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;
}
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);
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);
}
{
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;
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 |
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 */
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));
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;
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,
/* 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;
}
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);
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);
*/
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;
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);
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);
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);
endif # MODULES
DIST_SOURCES = $(lov-objs:.o=.c) lov_internal.h
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
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);
/* 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
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);
}
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;
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);
lov_proc_dir = NULL;
}
}
+#endif
RETURN(0);
}
if (rc)
GOTO(out_disc, rc);
}
-
+
class_export_put(exp);
RETURN (0);
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);
osc_obd->obd_name);
}
}
-
+
if (obd->obd_no_recov) {
/* Pass it on to our clients.
* XXX This should be an argument to disconnect,
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) {
}
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)
{
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);
}
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;
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) {
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)
{
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);
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);
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
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;
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)
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)
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);
}
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,
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;
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;
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;
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);
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) {
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) {
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;
{
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);
}
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,
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);
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);
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,
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,
req->rq_md->lsm_object_id, req->rq_idx, rc);
err = rc;
}
-
+
}
lov_fini_cancel_set(set);
RETURN(err);
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;
}
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
}
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);
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);
}
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,
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;
}
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)
rc = err;
}
RETURN(rc);
-#undef KEY_IS
}
int lov_test_and_clear_async_rc(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,
.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)
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);
{
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;
}
/* 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) &&
}
}
+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)
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;
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;
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);
}
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 "
req->rq_extent.start = rs;
req->rq_extent.end = re;
+ req->rq_extent.gid = -1;
lov_set_add_req(req, set);
}
req->rq_extent.start = rs;
req->rq_extent.end = re;
+ req->rq_extent.gid = -1;
lov_set_add_req(req, set);
}
#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)
{
.release = seq_release,
};
-#endif /* LPROCFS */
LPROCFS_INIT_VARS(lov, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
.depend
sources
fsfilt_ldiskfs.*
+fsfilt-ldiskfs.*
-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
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" $< > $@
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
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
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 */
}
}
#endif
- /* create/update logs for each stripe */
- nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS +
- EXT3_SINGLEDATA_TRANS_BLOCKS) * logs;
/* no break */
}
case FSFILT_OP_MKDIR:
/* 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
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;
}
#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;
/*
* 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;
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;
}
{
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;
}
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",
.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)
#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);
/* 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)
#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);
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);
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
}
}
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);
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;
}
/*
}
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);
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);
#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();
}
*
* 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.
*/
.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)
{
*
* 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.
*/
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;
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)
cleanup:
quotactl_test_4(tgt, sb);
- pop_ctxt(&saved, &tgt->obd_ctxt, NULL);
+ pop_ctxt(&saved, &tgt->obd_lvfs_ctxt, NULL);
return rc;
}
.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)
{
endif
DIST_SOURCES = $(mdc-objs:.o=.c) mdc_internal.h
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
#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 },
{ 0 }
};
+LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars)
#endif /* LPROCFS */
-LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars)
+#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,
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
+
+
* 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>
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;
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;
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;
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;
}
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) {
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);
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");
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)
{
#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:
#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;
{
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;
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;
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)
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@
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
#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);
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:
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 */
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;
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) {
med->med_mcd = mcd;
rc = mds_client_add(obd, &obd->u.mds, med, -1);
+ GOTO(out, rc);
out:
if (rc) {
{
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;
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);
!(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);
}
{
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;
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);
}
l_dput(dchild);
case 1:
- pop_ctxt(&saved, &obd->obd_ctxt, &uc);
+ pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc);
default: ;
}
return rc;
{
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;
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);
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;
}
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)) {
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))
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));
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(¤t->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);
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",
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);
}
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) {
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);
}
"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);
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);
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;
{
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);
}
out:
- RETURN(rc < 0 ? rc: item);
+ RETURN(rc < 0 ? rc : item);
err_llog:
/* cleanup all llogging subsystems */
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);
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);
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 */
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) {
must_relock++;
}
- obd_llog_finish(obd, 0);
-
mntput(mds->mds_vfsmnt);
mds->mds_sb = NULL;
}
spin_unlock_bh(&obd->obd_processing_task_lock);
- ll_clear_rdonly(save_dev);
-
+ lvfs_clear_rdonly(save_dev);
+
if (must_relock)
lock_kernel();
/* 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
#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)
{
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 },
{ "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 }
};
{ 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
#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
/* 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"
* 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)
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))
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,
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;
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;
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);
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);
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)
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;
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);
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;
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"
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;
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;
}
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);
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);
}
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;
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);
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);
}
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",
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);
}
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);
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);
/* 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,
#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);
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 */
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;
rec->ur_opcode = opcode;
rc = mds_unpackers[opcode](req, offset, rec);
+#if CRAY_PORTALS
+ rec->ur_fsuid = req->rq_uid;
+#endif
RETURN(rc);
}
{
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);
}
{
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);
}
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);
}
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;
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;
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)
&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);
}
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);
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) {
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);
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);
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);
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);
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);
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);
}
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);
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);
}
RETURN(0);
default:
+ CDEBUG(D_INFO, "unknown command %x\n", cmd);
RETURN(-EINVAL);
}
RETURN(0);
struct obd_device *obd;
struct obd_uuid *uuid;
unsigned long flags;
- int rc;
+ int rc = 0;
lock_kernel();
ptlrpc_daemonize();
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,
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",
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)
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)
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;
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;
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,
}
}
-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)
{
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;
cleanup_mfd:
mds_mfd_put(mfd);
- mds_mfd_destroy(mfd);
+ mds_mfd_unlink(mfd, 1);
cleanup_dentry:
return ERR_PTR(error);
}
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 };
RETURN(0);
}
+
if (OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_ALLOC_OBDO))
GOTO(out_ids, rc = -ENOMEM);
}
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);
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];
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.
mfd->mfd_handle.h_cookie);
}
+ mds_mfd_put(mfd);
+
out_dput:
if (put_child)
l_dput(dchild);
/* 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;
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);
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);
}
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);
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);
{
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;
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);
}
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;
/* 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) {
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);
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) {
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,
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 */
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;
}
}
- off = med->med_off;
+ off = med->med_lr_off;
transno = req->rq_reqmsg->transno;
if (rc != 0) {
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;
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) {
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();
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;
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);
}
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,
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) {
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,
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)
/* 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);
}
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 */
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);
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);
}
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;
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;
}
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
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);
}
}
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;
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;
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));
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) &&
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);
*
* 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
/* 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));
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);
}
RETURN(rc);
}
+EXPORT_SYMBOL(qctxt_adjust_qunit);
int
qctxt_wait_on_dqacq(struct obd_device *obd, struct lustre_quota_ctxt *qctxt,
RETURN(rc);
}
+EXPORT_SYMBOL(qctxt_wait_on_dqacq);
int
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)
{
spin_unlock(&qunit_hash_lock);
EXIT;
}
+EXPORT_SYMBOL(qctxt_cleanup);
*
* 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
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));
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();
}
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;
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++) {
}
up(&qinfo->qi_sem);
- pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+ pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
RETURN(rc);
}
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 */
}
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);
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));
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));
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;
}
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;
}
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);
}
}
- 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);
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
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 */
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 */
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);
}
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);
/* modules setup */
static struct miscdevice obd_psdev = {
.minor = OBD_MINOR,
- .name = "obd_psdev",
+ .name = "obd",
.fops = &obd_psdev_fops,
};
#else
#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);
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);
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);
);
}
+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)
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);
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)) {
#endif
{
struct obd_device *obd;
-#ifdef LPROCFS
+#ifdef __KERNEL__
struct proc_dir_entry *entry;
#endif
int err;
#ifdef __KERNEL__
obd_sysctl_init();
-#endif
-#ifdef LPROCFS
proc_lustre_root = proc_mkdir("lustre", proc_root_fs);
if (!proc_lustre_root) {
printk(KERN_ERR
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();
* 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)
#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);
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;
}
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;
}
static void obd_cleanup_qunit_cache(void)
{
+#ifdef HAVE_QUOTA_SUPPORT
int i;
ENTRY;
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)
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)
for (i = 0; i < NR_DQHASH; i++)
INIT_LIST_HEAD(qunit_hash + i);
spin_unlock(&qunit_hash_lock);
+#endif
RETURN(0);
}
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
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)
{
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)
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)
{
OBD_FREE(import, sizeof(*import));
EXIT;
}
+EXPORT_SYMBOL(class_import_put);
struct obd_import *class_new_import(void)
{
return imp;
}
+EXPORT_SYMBOL(class_new_import);
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
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
* again. */
int class_disconnect(struct obd_export *export)
{
+ int already_disconnected;
ENTRY;
if (export == NULL) {
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",
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);
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.
*/
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) {
}
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)
{
*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)
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)
wake_up(wake);
oig_release(oig);
}
+EXPORT_SYMBOL(oig_complete_one);
static int oig_done(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(¤t->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)";
+}
}
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);
}
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",
}
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);
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);
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));
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);
}
dput(fdentry);
- pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+ pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
RETURN(rc);
}
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;
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);
}
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);
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;
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);
}
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);
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);
{
struct llog_ctxt *ctxt;
struct llog_handle *handle;
- struct obd_run_ctxt saved;
+ struct lvfs_run_ctxt saved;
int rc;
ENTRY;
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);
#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)
}
case OBD_CFG_REC:
+ case PTL_CFG_REC: /* obsolete */
/* these are swabbed as they are consumed */
break;
break;
}
+ case LLOG_PAD_MAGIC:
/* ignore old pad records of type 0 */
case 0:
break;
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)
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;
.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)
{
#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)
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,
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,
}
EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status);
-#endif /* LPROCFS*/
-
EXPORT_SYMBOL(lprocfs_register);
EXPORT_SYMBOL(lprocfs_srch);
EXPORT_SYMBOL(lprocfs_remove);
EXPORT_SYMBOL(lprocfs_write_helper);
EXPORT_SYMBOL(lprocfs_write_u64_helper);
+#endif /* LPROCFS*/
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 */
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);
GOTO(out, rc = -EINVAL);
}
- /* The attach is our first obd reference */
+ /* Detach drops this */
atomic_set(&obd->obd_refcount, 1);
obd->obd_attached = 1;
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);
}
/* 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);
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);
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:
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);
}
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);
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 ? "..." : "");
{
int err = 0;
char *flag;
-
ENTRY;
+
OBD_RACE(OBD_FAIL_LDLM_RECOV_CLIENTS);
if (!obd->obd_set_up) {
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,
*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. */
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)
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);
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: {
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);
}
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);
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);
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;
+++ /dev/null
-/* -*- 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(¤t->fs->pwd->d_count),
- atomic_read(¤t->fs->pwd->d_inode->i_count),
- current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
- current->fs->pwdmnt,
- atomic_read(¤t->fs->pwdmnt->mnt_count));
- */
-
- save->fs = get_fs();
- LASSERT(atomic_read(¤t->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(¤t->fs->pwd->d_count),
- atomic_read(¤t->fs->pwd->d_inode->i_count),
- current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
- current->fs->pwdmnt,
- atomic_read(¤t->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(¤t->fs->pwd->d_count),
- atomic_read(¤t->fs->pwd->d_inode->i_count),
- current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
- current->fs->pwdmnt,
- atomic_read(¤t->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(¤t->fs->pwd->d_count),
- atomic_read(¤t->fs->pwd->d_inode->i_count),
- current->fs->pwd->d_name.len, current->fs->pwd->d_name.name,
- current->fs->pwdmnt,
- atomic_read(¤t->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);
-
};
#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! */
{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 }
};
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;
-}
modulefs_DATA = obdecho$(KMODEXT)
endif # MODULES
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
DIST_SOURCES = $(obdecho-objs:%.o=%.c)
static int echo_destroy_export(struct obd_export *exp)
{
ENTRY;
-
+
target_destroy_export(exp);
RETURN(0);
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;
}
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);
}
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);
}
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;
if (rc2 != 0 && rc == 0)
rc = rc2;
-
+
addr += OBD_ECHO_BLOCK_SIZE;
offset += OBD_ECHO_BLOCK_SIZE;
len -= OBD_ECHO_BLOCK_SIZE;
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)
* 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);
#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 }
{ 0 }
};
-#endif /* LPROCFS */
LPROCFS_INIT_VARS(echo, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
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
obdfilter-objs += filter_io_24.o
else
obdfilter-objs += filter_io_26.o
-obdfilter-objs += $(MDS)quota_context.o
endif # PATCHLEVEL
@INCLUDE_RULES@
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
#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>
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)
{
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)
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))
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,
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;
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,
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",
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;
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.*/
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",
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)
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);
/* 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);
GOTO(err_server_data, rc);
out:
- pop_ctxt(&saved, &obd->obd_ctxt, NULL);
+ pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
return(rc);
/* 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;
* 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)
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,
/* 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;
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);
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)
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)
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 "
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:
{
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 &&
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;
}
}
- 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);
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",
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();
/* 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;
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);
fed->fed_fcd = fcd;
rc = filter_client_add(obd, filter, fed, -1);
+ GOTO(cleanup, rc);
cleanup:
if (rc) {
} else {
class_export_put(exp);
}
-
+
RETURN(rc);
}
}
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;
}
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));
}
}
/* 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;
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));
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) {
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);
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);
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;
}
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)) {
}
}
- 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) {
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;
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);
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);
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);
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;
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;
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);
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);
}
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);
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);
}
/*
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);
*/
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)
{
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 *);
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;
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
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;
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);
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;
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);
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);
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;
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))
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)
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:;
/* 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;
}
#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 */
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);
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",
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 };
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);
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:
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);
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,
record_finish_io(dreq, rw, rc);
}
}
-
+
out:
wait_event(dreq->dr_wait, atomic_read(&dreq->dr_numreqs) == 0);
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 */
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;
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,
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,
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));
}
{
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;
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;
/* 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);
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");
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);
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");
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;
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) {
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);
}
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;
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);
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,
#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)
{
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 },
{ "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 }
};
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),
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),
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),
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),
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),
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;
&filter_brw_stats_fops, dev);
}
-
-
-#endif /* LPROCFS */
LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
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@
modulefs_DATA = osc$(KMODEXT)
endif
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
DIST_SOURCES = $(osc-objs:%.o=%.c) osc_internal.h
#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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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 },
{ "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 }
};
&osc_rpc_stats_fops, dev);
}
-#endif /* LPROCFS */
LPROCFS_INIT_VARS(osc, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
}
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) {
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;
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,
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;}
*
* 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
#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 {
{
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));
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);
+}
+
#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). */
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);
}
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,
/* 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);
}
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)
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));
}
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);
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);
}
* 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);
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);
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)
RETURN(0);
}
#endif
-#endif
static void osc_set_data_with_check(struct lustre_handle *lockh, void *data,
int flags)
{
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);
}
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)
{
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);
}
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;
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;
}
- 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);
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);
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;
rc = client_obd_cleanup(obd);
ptlrpcd_decref();
- obd_llog_finish(obd, 0);
RETURN(rc);
}
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,
.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,
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
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);
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);
modulefs_DATA = ost$(KMODEXT)
endif
-MOSTLYCLEANFILES = *.o *.ko *.mod.c
+MOSTLYCLEANFILES := @MOSTLYCLEANFILES@
DIST_SOURCES = $(ost-objs:%.o=%.c) ost_internal.h
#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 }
}
}
-#endif /* LPROCFS */
LPROCFS_INIT_VARS(ost, lprocfs_module_vars, lprocfs_obd_vars)
+#endif /* LPROCFS */
#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)
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)
{
int npages;
int nob = 0;
int rc;
- int i;
+ int i, do_checksum;
ENTRY;
if (OBD_FAIL_CHECK(OBD_FAIL_OST_BRW_READ_BULK))
}
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) {
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;
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:
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",
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))
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++) {
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) {
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)
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)
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);
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",
}
}
-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(¤t->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, };
# 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 */
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
$(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
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
desc->bd_md_h = PTL_INVALID_HANDLE;
desc->bd_portal = portal;
desc->bd_type = type;
-
+
return 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;
}
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) {
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 */
*
* 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;
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;
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);
}
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);
}
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);
}
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));
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);
/* 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 */
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;
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;
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,
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);
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: ");
}
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
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);
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. */
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);
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);
#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
int rc;
char str[20];
ptl_pid_t pid;
-
+
pid = ptl_get_pid();
/* We're not passing any limits yet... */
/* 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
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);
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);
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 */
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);
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)
{
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);
}
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);
RETURN(0);
}
+#ifdef __KERNEL__
static int ptlrpc_invalidate_import_thread(void *data)
{
struct obd_import *imp = data;
RETURN(0);
}
+#endif
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) {
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();
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;
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)
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);
}
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;
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)
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);
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)};
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)
rc = rc2;
out_pop:
- pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL);
+ pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL);
out:
RETURN(rc);
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;
}
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);
}
}
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
{
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;
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);
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);
}
{
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;
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;
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);
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)
int count, int *eof, void *data)
{
struct ptlrpc_service *svc = data;
-
+
*eof = 1;
return snprintf(page, count, "%d\n", svc->srv_n_history_rqbds);
}
int count, int *eof, void *data)
{
struct ptlrpc_service *svc = data;
-
+
*eof = 1;
return snprintf(page, count, "%d\n", svc->srv_max_history_rqbds);
}
unsigned long flags;
int val;
int rc = lprocfs_write_helper(buffer, count, &val);
-
+
if (rc < 0)
return rc;
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;
}
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 */
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;
}
e = e->next;
}
-
+
return -ENOENT;
}
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;
}
}
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;
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}
};
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;
&obddev->obd_svc_procroot,
&obddev->obd_svc_stats);
}
+EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd);
void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req)
{
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)
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;
/* 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;
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
request->rq_err = 1;
RETURN(-ENODEV);
}
-
+
connection = request->rq_import->imp_connection;
if (request->rq_bulk != NULL) {
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);
(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));
{
#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
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;
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;
}
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__
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;
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);
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 */
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;
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;
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");
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);
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 */
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 */
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);
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;
}
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);
} else {
CWARN("Invoked upcall %s %s %s\n",
- argv[0], argv[1], argv[2]);
+ argv[0], argv[1], argv[2]);
}
}
return;
}
spin_unlock_irqrestore(&imp->imp_lock, flags);
-
+
argv[0] = obd_lustre_upcall;
argv[1] = "FAILED_IMPORT";
argv[2] = imp->imp_target_uuid.uuid;
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]);
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);
void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req)
{
- int rc;
struct obd_import *imp= failed_req->rq_import;
unsigned long flags;
ENTRY;
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
/* 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) {
/* 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);
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);
-}
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 &&
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) {
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;
RETURN(did_something);
}
+#define ptlrpc_stop_all_threads(s) do {} while (0)
#else /* __KERNEL__ */
-RC_1_3_0_19
+RC_1_3_0_30
memhog
mmap_sanity
rmdirmany
+flock_test
writemany
--- /dev/null
+#!/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
[ "$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
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
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
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
$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
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}
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}
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`}
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
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}
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
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
${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
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
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
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
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
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
}
$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"
$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"
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
}
$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"
set -e
-# bug 2986
-ALWAYS_EXCEPT="20b"
+# bug 2986 5494
+ALWAYS_EXCEPT="20b 24"
LUSTRE=${LUSTRE:-`dirname $0`/..}
#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)"
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
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
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
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"
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
[ $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
}
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)"
--- /dev/null
+#!/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
fi
if [ "$I_MOUNTED" = "yes" ]; then
+ sync && sleep 2 && sync # wait for delete thread
sh llmountcleanup.sh || exit 29
fi
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:-:}
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"
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
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"
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"
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 ============================"
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
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
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
}
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
}
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
"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
#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
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."
$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
}
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
}
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}
#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.
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
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
}
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
}
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() {
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
}
+++ /dev/null
-/*
- * 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;
-}
#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
{
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;
#etc
OSTSIZE=${OSTSIZE:-100000}
STRIPECNT=${STRIPECNT:-1}
-STRIPE_BYTES=${STRIPE_BYTES:-$((1024 * 1024))}
+STRIPE_BYTES=${STRIPE_BYTES:-1048576}
OSDTYPE=${OSDTYPE:-obdfilter}
OSTFAILOVER=${OSTFAILOVER:-}
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
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;
}
}
+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));
* 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);
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));
}
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)
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);
print_err("write", filename, &cur, errno);
rc = errno;
break;
- }
+ }
nbytes += len;
- }
-
+ }
+
if (close(fd) < 0) {
print_err("close", filename, &cur, errno);
rc = errno;
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,
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;
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]) {
exit(2);
}
+ signal(SIGUSR1, usr1_handler);
+
for (i = 1; i <= threads; i++) {
rc = fork();
if (rc < 0) {
}
}
/* parent process */
- if (!o_quiet)
+ if (!o_quiet)
printf("%s will run for %ld minutes\n", cmdname, duration/60);
return (wait_for_threads(threads));
}
# 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:
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)
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
# 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
class LustreDB_XML(LustreDB):
def __init__(self, dom, root_node):
+ LustreDB.__init__(self)
+
# init xmlfile
self.dom_node = dom
self.root_node = root_node
user = "cn=Manager, fs=lustre",
pw = ""
):
+ LustreDB.__init__(self)
+
self._name = name
self._attrs = attrs
self._base = base
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
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)
--- /dev/null
+/* -*- 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(¶m);
+
+ rc = write(fd, param, size);
+
+ close(fd);
+ return rc;
+}
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])
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.
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)
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
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):
def abort_recovery(self, name):
cmds = """
ignore_errors
- device %s
+ device $%s
abort_recovery
quit""" % (name)
self.run(cmds)
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)
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):
# 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()
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."""
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)
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
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')
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)
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', '')
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)
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
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,
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)
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
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:
('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),
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 : "");
}
#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>
/* 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;
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)
__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;
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,
/* 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;
#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>
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;
}
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, ':'))) {
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 $@
}
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) {
#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>
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];
case 'r':
repeat_offset = 1;
break;
-
+
case 'x':
verify = 0;
break;
-
+
default:
fprintf (stderr, "Can't parse cmd '%s'\n",
argv[2]);
len = pages * getpagesize();
thr_offset = offset_pages * getpagesize();
stride = len;
-
+
if (thread) {
pthread_mutex_lock (&shared_data->mutex);
if (nthr_per_obj != 0) {
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;
}
#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
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);
(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));
(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",