From 784263efe38cf5b4ac73cafba3ef1ec6b730c35b Mon Sep 17 00:00:00 2001 From: eeb Date: Tue, 5 Jul 2005 18:35:10 +0000 Subject: [PATCH] * Compiles after merging b1_4 * 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 --- .../patches/ext3-extents-2.6.9-rhel4.patch | 4 +- .../patches/ext3-htree-dot-2.6.patch | 23 + .../kernel_patches/patches/ext3-ialloc-2.6.patch | 128 + .../patches/ext3-mballoc2-2.6-suse.patch | 7 +- .../patches/ext3-mballoc2-2.6.9-rhel4.patch | 7 +- .../kernel_patches/series/ldiskfs-2.6-rhel4.series | 1 + .../kernel_patches/series/ldiskfs-2.6-suse.series | 1 + ldiskfs/ldiskfs/Makefile.in | 10 +- ldiskfs/ldiskfs/autoMakefile.am | 8 +- lustre/ChangeLog | 194 +- lustre/Makefile.in | 12 +- lustre/autoMakefile.am | 31 +- lustre/autoconf/lustre-core.m4 | 61 +- lustre/autoconf/lustre-version.ac | 2 +- lustre/include/liblustre.h | 162 +- lustre/include/linux/lprocfs_status.h | 12 +- lustre/include/linux/lustre_cfg.h | 1 + lustre/include/linux/lustre_compat25.h | 20 +- lustre/include/linux/lustre_debug.h | 6 + lustre/include/linux/lustre_dlm.h | 16 +- lustre/include/linux/lustre_export.h | 8 +- lustre/include/linux/lustre_fsfilt.h | 33 +- lustre/include/linux/lustre_ha.h | 1 - lustre/include/linux/lustre_idl.h | 33 +- lustre/include/linux/lustre_lib.h | 131 +- lustre/include/linux/lustre_lite.h | 5 + lustre/include/linux/lustre_mds.h | 18 +- lustre/include/linux/lustre_net.h | 56 +- lustre/include/linux/lustre_quota.h | 108 +- lustre/include/linux/lvfs.h | 28 +- lustre/include/linux/lvfs_linux.h | 23 +- lustre/include/linux/obd.h | 11 +- lustre/include/linux/obd_class.h | 79 +- lustre/include/linux/obd_support.h | 100 +- lustre/include/lustre/lustre_user.h | 30 + .../kernel-2.4.21-rhel-2.4-i686-smp.config | 14 + .../kernel-2.4.21-rhel-2.4-i686.config | 56 +- .../kernel-2.4.21-rhel-2.4-ia64-smp.config | 3 + .../kernel-2.4.21-rhel-2.4-ia64.config | 5 +- .../kernel-2.4.21-rhel-2.4-x86_64-smp.config | 14 + .../kernel-2.4.21-rhel-2.4-x86_64.config | 25 +- .../kernel-2.6.5-2.6-suse-i686-bigsmp.config | 13 + .../kernel-2.6.5-2.6-suse-i686.config | 14 + .../kernel-2.6.5-2.6-suse-ia64-smp.config | 13 + .../kernel-2.6.5-2.6-suse-ia64.config | 13 + .../kernel-2.6.5-2.6-suse-x86_64-smp.config | 13 + .../kernel-2.6.5-2.6-suse-x86_64.config | 13 + .../kernel-2.6.9-2.6-rhel4-i686-smp.config | 1778 +- .../kernel-2.6.9-2.6-rhel4-i686.config | 1789 +- .../kernel_configs/uml-2.6.10-fc3.config | 659 + .../kernel_configs/uml-vanilla-2.4.24.config | 2 +- .../patches/blkdev_tunables-2.4.21-chaos.patch | 33 +- .../patches/dev_read_only-2.6-lnxi.patch | 111 +- .../patches/dev_read_only-2.6-suse.patch | 111 +- .../patches/dev_read_only_2.4.20-rh.patch | 120 +- .../patches/dev_read_only_2.4.21-chaos.patch | 115 +- lustre/kernel_patches/patches/elevator-cfq.patch | 20 + .../patches/export-show_task-2.4-cray.patch | 33 + .../patches/export-show_task-2.6-vanilla.patch | 8 +- .../patches/ext-2.4-patch-1-2.4.19-suse.patch | 18 +- .../kernel_patches/patches/ext-2.4-patch-1.patch | 18 +- .../patches/ext3-delete_thread-2.4.29.patch | 442 + .../patches/ext3-ea-in-inode-2.4.29.patch | 733 + .../patches/ext3-extents-2.4.21-chaos.patch | 6 +- .../patches/ext3-extents-2.4.21-suse2.patch | 20 +- .../patches/ext3-extents-2.4.24.patch | 6 +- .../patches/ext3-extents-2.4.29.patch | 2851 + .../patches/ext3-extents-2.6.9-rhel4.patch | 4 +- .../patches/ext3-htree-2.4.21-rhel.patch | 31 +- .../patches/ext3-htree-2.4.22-rh.patch | 18 +- .../kernel_patches/patches/ext3-htree-2.4.29.patch | 2496 + .../patches/ext3-htree-dot-2.6.5-suse.patch | 23 + .../patches/ext3-htree-dot-2.6.patch | 23 + .../patches/ext3-ialloc-2.4.24.patch | 238 + .../kernel_patches/patches/ext3-ialloc-2.6.patch | 128 + .../patches/ext3-mballoc2-2.6-suse.patch | 7 +- .../patches/ext3-mballoc2-2.6.9-rhel4.patch | 7 +- .../patches/fsprivate-2.4-suse.patch | 10 + lustre/kernel_patches/patches/fsprivate-2.4.patch | 10 + lustre/kernel_patches/patches/fsprivate-2.6.patch | 10 + .../patches/gfp_debug-2.4.21-rhel.patch | 77 + .../grab_cache_page_nowait_gfp-2.4.21-suse2.patch | 85 + .../patches/invalidate_show-2.4.29.patch | 107 + .../kernel_patches/patches/kallsyms-2.4.29.patch | 689 + .../patches/linux-2.4.24-jbd-handle-EIO.patch | 51 + .../patches/linux-2.4.29-xattr-0.8.54.patch | 5362 ++ lustre/kernel_patches/patches/lustre_version.patch | 3 +- .../patches/nfs_export_kernel-2.4.20-hp.patch | 16 +- .../patches/nfs_export_kernel-2.4.29.patch | 743 + .../patches/nfs_export_kernel-2.4.29.patch-1 | 730 + .../patches/nfs_statfs-toomanyfiles-rhel-2.4.patch | 30 + lustre/kernel_patches/patches/nfsd_iallocsem.patch | 19 + lustre/kernel_patches/patches/qsnet-rhel-2.4.patch | 93733 ++++++++++++++++++ lustre/kernel_patches/patches/qsnet-suse-2.6.patch | 94821 +++++++++++++++++++ .../patches/small_scatterlist-2.4.21-rhel.patch | 272 +- .../patches/statfs64-cast-unsigned-2.4-rhel.patch | 28 + .../patches/tcp-zero-copy-2.4.21-suse-171.patch | 456 - .../patches/uml-exprt-clearuser.patch | 24 + .../patches/uml-patch-2.4.24-1.patch | 2 +- .../patches/uml-patch-2.4.29-1.patch | 46719 +++++++++ .../patches/vfs_intent-2.4.21-rhel.patch | 226 +- .../patches/vfs_intent-2.4.29-vanilla.patch | 1833 + .../kernel_patches/patches/vfs_races-2.6-fc3.patch | 64 + lustre/kernel_patches/series/2.6-fc3.series | 3 + lustre/kernel_patches/series/2.6-rhel4.series | 1 + lustre/kernel_patches/series/2.6-suse-lnxi.series | 3 + lustre/kernel_patches/series/bgl-2.4.19 | 1 + .../kernel_patches/series/ldiskfs-2.6-rhel4.series | 1 + .../kernel_patches/series/ldiskfs-2.6-suse.series | 1 + lustre/kernel_patches/series/rhel-2.4.21 | 8 +- lustre/kernel_patches/series/suse-2.4.21-cray | 7 + lustre/kernel_patches/series/suse-2.4.21-jvn | 3 +- lustre/kernel_patches/series/vanilla-2.4.24 | 4 + lustre/kernel_patches/series/vanilla-2.4.29 | 43 + lustre/kernel_patches/series/vanilla-2.4.29-uml | 47 + lustre/kernel_patches/targets/2.6-rhel4.target.in | 3 +- lustre/kernel_patches/targets/2.6-suse.target.in | 1 + .../kernel_patches/targets/2.6-vanilla.target.in | 1 + .../kernel_patches/targets/hp_pnnl-2.4.target.in | 1 + lustre/kernel_patches/targets/rh-2.4.target.in | 1 + lustre/kernel_patches/targets/rhel-2.4.target.in | 5 +- lustre/kernel_patches/targets/sles-2.4.target.in | 1 + .../kernel_patches/targets/suse-2.4.21-2.target.in | 1 + lustre/ldiskfs/Makefile.in | 10 +- lustre/ldiskfs/autoMakefile.am | 8 +- lustre/ldiskfs/lustre_quota_fmt.c | 40 +- lustre/ldiskfs/quotafmt_test.c | 94 +- lustre/ldlm/Makefile.am | 2 +- lustre/ldlm/ldlm_extent.c | 166 +- lustre/ldlm/ldlm_flock.c | 36 +- lustre/ldlm/ldlm_internal.h | 4 + lustre/ldlm/ldlm_lib.c | 93 +- lustre/ldlm/ldlm_lock.c | 52 +- lustre/ldlm/ldlm_lockd.c | 67 +- lustre/ldlm/ldlm_request.c | 95 +- lustre/ldlm/ldlm_resource.c | 56 +- lustre/liblustre/Makefile.am | 23 +- lustre/liblustre/dir.c | 89 +- lustre/liblustre/file.c | 159 +- lustre/liblustre/genlib.sh | 84 +- lustre/liblustre/llite_lib.c | 367 +- lustre/liblustre/llite_lib.h | 116 +- lustre/liblustre/lutil.c | 180 +- lustre/liblustre/lutil.h | 1 - lustre/liblustre/namei.c | 106 +- lustre/liblustre/rw.c | 886 +- lustre/liblustre/super.c | 797 +- lustre/liblustre/tests/Makefile.am | 40 +- lustre/liblustre/tests/echo_test.c | 92 +- lustre/liblustre/tests/recovery_small.c | 31 +- lustre/liblustre/tests/replay_single.c | 45 +- lustre/liblustre/tests/sanity.c | 716 +- lustre/liblustre/tests/test_common.c | 106 +- lustre/liblustre/tests/test_common.h | 7 + lustre/llite/autoMakefile.am | 2 +- lustre/llite/dcache.c | 14 +- lustre/llite/dir.c | 34 +- lustre/llite/file.c | 187 +- lustre/llite/llite_internal.h | 38 +- lustre/llite/llite_lib.c | 113 +- lustre/llite/llite_mmap.c | 4 +- lustre/llite/lproc_llite.c | 49 +- lustre/llite/namei.c | 16 +- lustre/llite/rw.c | 175 +- lustre/llite/super.c | 4 +- lustre/lov/autoMakefile.am | 2 +- lustre/lov/lov_internal.h | 5 + lustre/lov/lov_merge.c | 11 +- lustre/lov/lov_obd.c | 449 +- lustre/lov/lov_pack.c | 20 +- lustre/lov/lov_qos.c | 31 + lustre/lov/lov_request.c | 36 +- lustre/lov/lproc_lov.c | 8 +- lustre/lvfs/.cvsignore | 1 + lustre/lvfs/Makefile.in | 7 +- lustre/lvfs/autoMakefile.am | 15 +- lustre/lvfs/fsfilt_ext3.c | 563 +- lustre/lvfs/lvfs_common.c | 2 +- lustre/lvfs/lvfs_linux.c | 110 +- lustre/lvfs/lvfs_userfs.c | 8 +- lustre/lvfs/quotacheck_test.c | 15 +- lustre/lvfs/quotactl_test.c | 21 +- lustre/mdc/autoMakefile.am | 2 +- lustre/mdc/lproc_mdc.c | 7 +- lustre/mdc/mdc_internal.h | 25 + lustre/mdc/mdc_lib.c | 12 +- lustre/mdc/mdc_locks.c | 12 +- lustre/mdc/mdc_request.c | 127 +- lustre/mds/Makefile.in | 2 +- lustre/mds/autoMakefile.am | 5 +- lustre/mds/handler.c | 381 +- lustre/mds/lproc_mds.c | 128 +- lustre/mds/mds_fs.c | 153 +- lustre/mds/mds_internal.h | 51 +- lustre/mds/mds_lib.c | 6 +- lustre/mds/mds_log.c | 25 +- lustre/mds/mds_lov.c | 66 +- lustre/mds/mds_open.c | 126 +- lustre/mds/mds_reint.c | 112 +- lustre/mds/mds_unlink_open.c | 17 +- lustre/mds/quota_context.c | 22 +- lustre/mds/quota_master.c | 80 +- lustre/obdclass/autoMakefile.am | 2 +- lustre/obdclass/class_obd.c | 127 +- lustre/obdclass/genops.c | 332 +- lustre/obdclass/llog.c | 2 +- lustre/obdclass/llog_ioctl.c | 1 + lustre/obdclass/llog_lvfs.c | 25 +- lustre/obdclass/llog_obd.c | 7 +- lustre/obdclass/llog_swab.c | 6 + lustre/obdclass/llog_test.c | 8 +- lustre/obdclass/lprocfs_status.c | 16 +- lustre/obdclass/obd_config.c | 151 +- lustre/obdclass/simple.c | 266 - lustre/obdclass/sysctl.c | 38 +- lustre/obdclass/uuid.c | 23 - lustre/obdecho/autoMakefile.am | 2 +- lustre/obdecho/echo.c | 16 +- lustre/obdecho/lproc_echo.c | 8 +- lustre/obdfilter/Makefile.in | 3 +- lustre/obdfilter/autoMakefile.am | 2 +- lustre/obdfilter/filter.c | 436 +- lustre/obdfilter/filter_internal.h | 89 +- lustre/obdfilter/filter_io.c | 21 +- lustre/obdfilter/filter_io_24.c | 81 +- lustre/obdfilter/filter_io_26.c | 160 +- lustre/obdfilter/filter_log.c | 12 +- lustre/obdfilter/filter_lvb.c | 3 +- lustre/obdfilter/filter_san.c | 17 +- lustre/obdfilter/lproc_obdfilter.c | 152 +- lustre/osc/Makefile.in | 2 +- lustre/osc/autoMakefile.am | 2 +- lustre/osc/lproc_osc.c | 87 +- lustre/osc/osc_create.c | 2 + lustre/osc/osc_internal.h | 45 +- lustre/osc/osc_quota.c | 122 +- lustre/osc/osc_request.c | 307 +- lustre/ost/autoMakefile.am | 2 +- lustre/ost/lproc_ost.c | 7 +- lustre/ost/ost_handler.c | 211 +- lustre/ost/ost_internal.h | 17 + lustre/ptlrpc/Makefile.in | 5 + lustre/ptlrpc/autoMakefile.am | 7 +- lustre/ptlrpc/client.c | 100 +- lustre/ptlrpc/events.c | 18 +- lustre/ptlrpc/import.c | 109 +- lustre/ptlrpc/llog_client.c | 8 +- lustre/ptlrpc/llog_server.c | 36 +- lustre/ptlrpc/lproc_ptlrpc.c | 46 +- lustre/ptlrpc/niobuf.c | 8 +- lustre/ptlrpc/pack_generic.c | 38 +- lustre/ptlrpc/pers.c | 6 + lustre/ptlrpc/pinger.c | 94 +- lustre/ptlrpc/ptlrpc_internal.h | 3 +- lustre/ptlrpc/ptlrpc_module.c | 5 - lustre/ptlrpc/recov_thread.c | 8 + lustre/ptlrpc/recover.c | 55 +- lustre/ptlrpc/service.c | 28 +- lustre/tests/.RC_CURRENT.tag | 2 +- lustre/tests/.cvsignore | 1 + lustre/tests/2ost.sh | 52 + lustre/tests/acceptance-small.sh | 91 +- lustre/tests/cfg/local.sh | 4 +- lustre/tests/cfg/mdev.sh | 4 +- lustre/tests/echo.sh | 7 + lustre/tests/llmount.sh | 5 +- lustre/tests/lov.sh | 2 +- lustre/tests/mcr-individual-ost-nogw-config.sh | 46 - lustre/tests/mcr-mds-failover-config.sh | 50 - lustre/tests/mcr-routed-config.sh | 93 - lustre/tests/mcrlov.sh | 52 - lustre/tests/mount2fs.sh | 15 +- lustre/tests/oos.sh | 2 +- lustre/tests/quota_sanity.sh | 202 +- lustre/tests/recovery-small.sh | 132 +- lustre/tests/replay-single.sh | 13 +- lustre/tests/routed.sh | 157 + lustre/tests/runtests | 1 + lustre/tests/sanity.sh | 242 +- lustre/tests/sanityN.sh | 14 + lustre/tests/test-framework.sh | 33 +- lustre/tests/test.c | 101 - lustre/tests/test_brw.c | 16 +- lustre/tests/uml.sh | 9 +- lustre/tests/writemany.c | 76 +- lustre/utils/Lustre/lustredb.py | 181 +- lustre/utils/Makefile.am | 26 +- lustre/utils/l_getgroups.c | 104 + lustre/utils/lconf | 192 +- lustre/utils/lfs.c | 12 +- lustre/utils/liblustreapi.c | 92 +- lustre/utils/llmount.c | 7 +- lustre/utils/lrun | 4 + lustre/utils/lustre_cfg.c | 6 +- lustre/utils/obd.c | 18 +- lustre/utils/parser.c | 46 +- lustre/utils/wirecheck.c | 1 + lustre/utils/wiretest.c | 4 +- 298 files changed, 263250 insertions(+), 9786 deletions(-) create mode 100644 ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch create mode 100644 ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch create mode 100644 lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config create mode 100644 lustre/kernel_patches/patches/elevator-cfq.patch create mode 100644 lustre/kernel_patches/patches/export-show_task-2.4-cray.patch create mode 100644 lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/ext3-extents-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/ext3-htree-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch create mode 100644 lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch create mode 100644 lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch create mode 100644 lustre/kernel_patches/patches/ext3-ialloc-2.6.patch create mode 100644 lustre/kernel_patches/patches/fsprivate-2.4-suse.patch create mode 100644 lustre/kernel_patches/patches/fsprivate-2.4.patch create mode 100644 lustre/kernel_patches/patches/fsprivate-2.6.patch create mode 100644 lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch create mode 100644 lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch create mode 100644 lustre/kernel_patches/patches/invalidate_show-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/kallsyms-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch create mode 100644 lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch create mode 100644 lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch create mode 100644 lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 create mode 100644 lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch create mode 100644 lustre/kernel_patches/patches/nfsd_iallocsem.patch create mode 100644 lustre/kernel_patches/patches/qsnet-rhel-2.4.patch create mode 100644 lustre/kernel_patches/patches/qsnet-suse-2.6.patch create mode 100644 lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch delete mode 100644 lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch create mode 100644 lustre/kernel_patches/patches/uml-exprt-clearuser.patch create mode 100644 lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch create mode 100644 lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch create mode 100644 lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch create mode 100644 lustre/kernel_patches/series/vanilla-2.4.29 create mode 100644 lustre/kernel_patches/series/vanilla-2.4.29-uml delete mode 100644 lustre/obdclass/simple.c create mode 100644 lustre/tests/2ost.sh delete mode 100755 lustre/tests/mcr-individual-ost-nogw-config.sh delete mode 100755 lustre/tests/mcr-mds-failover-config.sh delete mode 100755 lustre/tests/mcr-routed-config.sh delete mode 100755 lustre/tests/mcrlov.sh create mode 100644 lustre/tests/routed.sh delete mode 100755 lustre/tests/test.c create mode 100644 lustre/utils/l_getgroups.c diff --git a/ldiskfs/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch b/ldiskfs/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch index c8fc99a..3b873c2 100644 --- a/ldiskfs/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch +++ b/ldiskfs/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch @@ -2556,16 +2556,16 @@ Index: linux-stage/include/linux/ext3_fs.h --- linux-stage.orig/include/linux/ext3_fs.h 2005-02-25 14:53:56.424908168 +0200 +++ linux-stage/include/linux/ext3_fs.h 2005-02-25 15:39:12.841950008 +0200 @@ -186,8 +186,9 @@ + #define EXT3_NOTAIL_FL 0x00008000 /* don't merge file tail */ #define EXT3_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define EXT3_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ - #define EXT3_RESERVED_FL 0x80000000 /* reserved for ext3 lib */ +#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ + #define EXT3_RESERVED_FL 0x80000000 /* reserved for ext3 lib */ -#define EXT3_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT3_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ #define EXT3_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */ - /* @@ -237,6 +238,9 @@ #endif #define EXT3_IOC_GETRSVSZ _IOR('f', 5, long) diff --git a/ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch b/ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch new file mode 100644 index 0000000..9192112 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/ext3-htree-dot-2.6.patch @@ -0,0 +1,23 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c 2005-04-04 05:09:18.000000000 -0600 +@@ -926,8 +926,16 @@ + struct inode *dir = dentry->d_parent->d_inode; + + sb = dir->i_sb; +- if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) +- return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); diff --git a/ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch b/ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch new file mode 100644 index 0000000..15d37a9 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/ext3-ialloc-2.6.patch @@ -0,0 +1,128 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/ialloc.c 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c 2005-05-16 14:18:29.000000000 -0600 +@@ -352,13 +352,17 @@ + return -1; + } + +-static int find_group_other(struct super_block *sb, struct inode *parent) ++static int find_group_other(struct super_block *sb, struct inode *parent, ++ int mode) + { + int parent_group = EXT3_I(parent)->i_block_group; ++ struct ext3_sb_info *sbi = EXT3_SB(sb); + int ngroups = EXT3_SB(sb)->s_groups_count; + struct ext3_group_desc *desc; + struct buffer_head *bh; + int group, i; ++ int best_group = -1; ++ int avefreeb, freeb, best_group_freeb = 0; + + /* + * Try to place the inode in its parent directory +@@ -366,9 +370,9 @@ + group = parent_group; + desc = ext3_get_group_desc (sb, group, &bh); + if (desc && le16_to_cpu(desc->bg_free_inodes_count) && +- le16_to_cpu(desc->bg_free_blocks_count)) ++ (!S_ISREG(mode) || le16_to_cpu(desc->bg_free_blocks_count))) + return group; +- ++ avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) / ngroups; + /* + * We're going to place this inode in a different blockgroup from its + * parent. We want to cause files in a common directory to all land in +@@ -381,33 +385,47 @@ + group = (group + parent->i_ino) % ngroups; + + /* +- * Use a quadratic hash to find a group with a free inode and some free +- * blocks. ++ * Use a quadratic hash to find a group with a free inode and ++ * average number of free blocks. + */ + for (i = 1; i < ngroups; i <<= 1) { + group += i; + if (group >= ngroups) + group -= ngroups; + desc = ext3_get_group_desc (sb, group, &bh); +- if (desc && le16_to_cpu(desc->bg_free_inodes_count) && +- le16_to_cpu(desc->bg_free_blocks_count)) ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ if (!S_ISREG(mode)) ++ return group; ++ if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb) + return group; + } + + /* +- * That failed: try linear search for a free inode, even if that group +- * has no free blocks. ++ * That failed: start from last group used to allocate inode ++ * try linear search for a free inode and prefereably ++ * free blocks. + */ +- group = parent_group; ++ group = sbi->s_last_alloc_group; ++ if (group == -1) ++ group = parent_group; ++ + for (i = 0; i < ngroups; i++) { + if (++group >= ngroups) + group = 0; + desc = ext3_get_group_desc (sb, group, &bh); +- if (desc && le16_to_cpu(desc->bg_free_inodes_count)) +- return group; ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ freeb = le16_to_cpu(desc->bg_free_blocks_count); ++ if (freeb > best_group_freeb) { ++ best_group_freeb = freeb; ++ best_group = group; ++ if (freeb >= avefreeb || !S_ISREG(mode)) ++ break; ++ } + } +- +- return -1; ++ sbi->s_last_alloc_group = best_group; ++ return best_group; + } + + /* +@@ -454,7 +472,7 @@ + else + group = find_group_orlov(sb, dir); + } else +- group = find_group_other(sb, dir); ++ group = find_group_other(sb, dir, mode); + + err = -ENOSPC; + if (group == -1) +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/super.c 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c 2005-05-16 14:17:14.000000000 -0600 +@@ -1297,6 +1297,7 @@ + percpu_counter_init(&sbi->s_dirs_counter); + bgl_lock_init(&sbi->s_blockgroup_lock); + ++ sbi->s_last_alloc_group = -1; + for (i = 0; i < db_count; i++) { + block = descriptor_loc(sb, logic_sb_block, i); + sbi->s_group_desc[i] = sb_bread(sb, block); +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/include/linux/ext3_fs_sb.h 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h 2005-05-16 14:17:14.000000000 -0600 +@@ -59,6 +59,8 @@ + struct percpu_counter s_freeinodes_counter; + struct percpu_counter s_dirs_counter; + struct blockgroup_lock s_blockgroup_lock; ++ /* Last group used to allocate inode */ ++ int s_last_alloc_group; + + /* root of the per fs reservation window tree */ + spinlock_t s_rsv_window_lock; diff --git a/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch b/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch index 4de2b32..d38fb54 100644 --- a/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch +++ b/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch @@ -2,7 +2,7 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c =================================================================== --- linux-2.6.5-suse.orig/fs/ext3/mballoc.c 2005-03-02 22:42:20.659360368 +0300 +++ linux-2.6.5-suse/fs/ext3/mballoc.c 2005-03-11 16:13:13.000000000 +0300 -@@ -0,0 +1,1863 @@ +@@ -0,0 +1,1864 @@ +/* + * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas @@ -932,8 +932,9 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c + * We've been searching too long. Let's try to allocate + * the best chunk we've found so far + */ -+ printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n", -+ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); ++ ext3_warning(inode->i_sb, __FUNCTION__, ++ "too long searching: got %d want %d\n", ++ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); + ext3_mb_try_best_found(&ac, &e3b); + if (ac.ac_status != AC_STATUS_FOUND) { + /* diff --git a/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch b/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch index dc6ffe3..24b2595 100644 --- a/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch +++ b/ldiskfs/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch @@ -2,7 +2,7 @@ Index: linux-stage/fs/ext3/mballoc.c =================================================================== --- linux-stage.orig/fs/ext3/mballoc.c 2005-02-25 17:28:41.836311072 +0200 +++ linux-stage/fs/ext3/mballoc.c 2005-02-25 17:28:41.859307576 +0200 -@@ -0,0 +1,1860 @@ +@@ -0,0 +1,1861 @@ +/* + * Copyright (c) 2003, Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas @@ -932,8 +932,9 @@ Index: linux-stage/fs/ext3/mballoc.c + * We've been searching too long. Let's try to allocate + * the best chunk we've found so far + */ -+ printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n", -+ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); ++ ext3_warning(inode->i_sb, __FUNCTION__, ++ "too long searching: got %d want %d\n", ++ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); + ext3_mb_try_best_found(&ac, &e3b); + if (ac.ac_status != AC_STATUS_FOUND) { + /* diff --git a/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series b/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series index 70e7b12..0980162 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-2.6-rhel4.series @@ -9,3 +9,4 @@ ext3-include-fixes-2.6-rhel4.patch ext3-extents-2.6.9-rhel4.patch ext3-mballoc2-2.6.9-rhel4.patch ext3-nlinks-2.6.7.patch +ext3-htree-dot-2.6.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-2.6-suse.series b/ldiskfs/kernel_patches/series/ldiskfs-2.6-suse.series index fd05c25..c44eea3 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-2.6-suse.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-2.6-suse.series @@ -10,3 +10,4 @@ ext3-extents-2.6.5.patch ext3-mballoc2-2.6-suse.patch ext3-nlinks-2.6.7.patch ext3-rename-reserve-2.6-suse.patch +ext3-htree-dot-2.6.5-suse.patch diff --git a/ldiskfs/ldiskfs/Makefile.in b/ldiskfs/ldiskfs/Makefile.in index be51da2..7236410 100644 --- a/ldiskfs/ldiskfs/Makefile.in +++ b/ldiskfs/ldiskfs/Makefile.in @@ -1,6 +1,6 @@ default: all -MODULES := ldiskfs quotafmt_test +MODULES := ldiskfs #quotafmt_test # copy makefile over to not break patches ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile) @@ -11,13 +11,13 @@ linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h) ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c)) new_sources := iopen.c iopen.h extents.c mballoc.c new_headers := ext3_extents.h -quotafmt_sources := lustre_quota_fmt.c -quotafmt_headers := lustre_quota_fmt.h +#quotafmt_sources := lustre_quota_fmt.c +#quotafmt_headers := lustre_quota_fmt.h ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers) -ldiskfs_sources := $(ldiskfs_patched_sources) $(quotafmt_sources) $(quotafmt_headers) +ldiskfs_sources := $(ldiskfs_patched_sources) #$(quotafmt_sources) $(quotafmt_headers) ldiskfs-objs := $(filter %.o,$(ldiskfs_sources:.c=.o)) -quotafmt-objs := quotafmt_test.o +#quotafmt-objs := quotafmt_test.o EXTRA_PRE_CFLAGS := -I@LINUX@/fs -I@LUSTRE@ -I@LUSTRE@/ldiskfs diff --git a/ldiskfs/ldiskfs/autoMakefile.am b/ldiskfs/ldiskfs/autoMakefile.am index 4f9e784..53ca41e 100644 --- a/ldiskfs/ldiskfs/autoMakefile.am +++ b/ldiskfs/ldiskfs/autoMakefile.am @@ -41,10 +41,12 @@ if USE_QUILT cd linux-stage && quilt setup -l ../$(series) -d ../$(patches) cd linux-stage && quilt push -a -q else + @echo -n "Applying ext3 patches:" @cd linux-stage && for i in $$(<../$(series)) ; do \ - echo "patch -p1 < ../$(patches)/$$i" ; \ - patch -p1 < ../$(patches)/$$i || exit 1 ; \ + echo -n " $$i" ; \ + patch -s -p1 < ../$(patches)/$$i || exit 1 ; \ done + @echo endif mkdir linux @echo -n "Replacing 'ext3' with 'ldiskfs':" @@ -70,7 +72,7 @@ foo-check: @echo "ldiskfs_OBJECTS: $(ldiskfs_OBJECTS)" @echo "ldiskfs_LDADD: $(ldiskfs_LDADD)" -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ CLEANFILES = sources $(notdir $(linux_headers) $(ext3_headers) $(ext3_sources) $(new_sources) $(new_headers)) EXTRA_DIST := lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c diff --git a/lustre/ChangeLog b/lustre/ChangeLog index ace07ea..0fb9dff 100644 --- a/lustre/ChangeLog +++ b/lustre/ChangeLog @@ -1,4 +1,172 @@ tbd Cluster File Systems, Inc. + * 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. + * 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 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. * version 1.4.2 NOTE: Lustre 1.4.2 uses an incompatible network protocol than previous versions of Lustre. Please update all servers and clients to @@ -19,7 +187,18 @@ tbd Cluster File Systems, Inc. - don't hold i_size_sem in ll_nopage() and ll_ap_refresh_count (6077) - don't hold client locks on temporary worklist from l_lru (5666) - handle IO errors in 2.6 obdfilter bio completion routine (6046) - * miscellania + - automatically evict dead clients (5921) + - Update file size properly in create+truncate+fstat case (6196) + - Do not unhash mountpoint dentries, do not allow removal of + mountpoints (5907) + - Avoid lock ordering deadlock issue with write/truncate (6203,5654) + - reserve enough journal credits in fsfilt_start_log for setattr (4554) + - ldlm_enqueue freed-export error path would always LBUG (6149,6184) + - don't reference lr_lvb_data until after we hold lr_lvb_sem (6170) + - don't overwrite last_rcvd if there is a *_client_add() error (6086) + - Correctly handle reads of files with no objects (6243) + - lctl recover will also mark a device active if deactivate used (5933) + * miscellania - by default create 1 inode per 4kB space on MDS, per 16kB on OSTs - allow --write-conf on an MDS with different nettype than client (5619) - don't write config llogs to MDS for mounts not from that MDS (5617) @@ -36,7 +215,16 @@ tbd Cluster File Systems, Inc. - 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. * version 1.4.1 * bug fixes @@ -101,7 +289,7 @@ tbd Cluster File Systems, Inc. - 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) diff --git a/lustre/Makefile.in b/lustre/Makefile.in index 789f656..d6e7684 100644 --- a/lustre/Makefile.in +++ b/lustre/Makefile.in @@ -1,15 +1,13 @@ -@LDISKFS_TRUE@subdir-m += ldiskfs +@LDISKFS_TRUE@subdir-m += ldiskfs subdir-m += lvfs subdir-m += obdclass subdir-m += lov subdir-m += ptlrpc -subdir-m += obdecho subdir-m += osc -subdir-m += mdc -subdir-m += mds -subdir-m += obdfilter -subdir-m += ost -subdir-m += llite +subdir-m += obdecho + +@SERVER_TRUE@subdir-m += mds obdfilter ost +@CLIENT_TRUE@subdir-m += mdc llite @INCLUDE_RULES@ diff --git a/lustre/autoMakefile.am b/lustre/autoMakefile.am index c94d7cc..65f636b 100644 --- a/lustre/autoMakefile.am +++ b/lustre/autoMakefile.am @@ -5,9 +5,34 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = include ldiskfs lvfs obdclass lov ldlm ptlrpc \ - obdecho osc mdc mds obdfilter ost llite \ - liblustre doc utils tests conf scripts autoconf +ALWAYS_SUBDIRS := include lvfs obdclass ldlm ptlrpc osc lov obdecho \ + doc utils tests conf scripts autoconf + +SERVER_SUBDIRS := ldiskfs obdfilter ost mds + +CLIENT_SUBDIRS := mdc llite + +LIBLUSTRE_SUBDIRS := liblustre + +SUBDIRS := $(ALWAYS_SUBDIRS) + +if SERVER +SUBDIRS += $(SERVER_SUBDIRS) +endif + +if CLIENT +SUBDIRS += $(CLIENT_SUBDIRS) +endif + +# this needs to be after the client subdirs +if LIBLUSTRE +if !CLIENT +SUBDIRS += $(CLIENT_SUBDIRS) +endif +SUBDIRS += $(LIBLUSTRE_SUBDIRS) +endif + +DIST_SUBDIRS := $(ALWAYS_SUBDIRS) $(SERVER_SUBDIRS) $(CLIENT_SUBDIRS) $(LIBLUSTRE_SUBDIRS) EXTRA_DIST = BUGS FDL kernel_patches diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4 index c8ac15b..6d639ec 100644 --- a/lustre/autoconf/lustre-core.m4 +++ b/lustre/autoconf/lustre-core.m4 @@ -155,6 +155,25 @@ fi ]) # +# LC_FUNC_FILEMAP_FDATASYNC +# +# if filemap_fdatasync() exists +# +AC_DEFUN([LC_FUNC_FILEMAP_FDATAWRITE], +[AC_MSG_CHECKING([whether filemap_fdatawrite() is defined]) +LB_LINUX_TRY_COMPILE([ + #include +],[ + int (*foo)(struct address_space *)= filemap_fdatawrite; +],[ + AC_MSG_RESULT([yes]) + AC_DEFINE(HAVE_FILEMAP_FDATAWRITE, 1, [filemap_fdatawrite() found]) +],[ + AC_MSG_RESULT([no]) +]) +]) + +# # LC_FUNC_DIRECT_IO # # if direct_IO takes a struct file argument @@ -389,7 +408,9 @@ AC_DEFINE_UNQUOTED(OBD_MAX_IOCTL_BUFFER, $OBD_BUFFER_SIZE, [IOCTL Buffer Size]) # Lustre linux kernel checks # AC_DEFUN([LC_PROG_LINUX], -[LC_CONFIG_BACKINGFS +[if test x$enable_server = xyes ; then + LC_CONFIG_BACKINGFS +fi LC_CONFIG_PINGER LC_STRUCT_KIOBUF @@ -402,9 +423,30 @@ LC_STRUCT_INODE LC_FUNC_REGISTER_CACHE LC_FUNC_GRAB_CACHE_PAGE_NOWAIT_GFP LC_FUNC_DEV_SET_RDONLY +LC_FUNC_FILEMAP_FDATAWRITE ]) # +# LC_CONFIG_CLIENT_SERVER +# +# Build client/server sides of Lustre +# +AC_DEFUN([LC_CONFIG_CLIENT_SERVER], +[AC_MSG_CHECKING([whether to build Lustre server support]) +AC_ARG_ENABLE([server], + AC_HELP_STRING([--disable-server], + [disable Lustre server support]), + [],[enable_server='yes']) +AC_MSG_RESULT([$enable_server]) + +AC_MSG_CHECKING([whether to build Lustre client support]) +AC_ARG_ENABLE([client], + AC_HELP_STRING([--disable-client], + [disable Lustre client support]), + [],[enable_client='yes']) +AC_MSG_RESULT([$enable_client])]) + +# # LC_CONFIG_LIBLUSTRE # # whether to build liblustre @@ -439,8 +481,17 @@ AC_DEFUN([LC_CONFIGURE], AC_CHECK_HEADERS([asm/page.h sys/user.h stdint.h]) # include/lustre/lustre_user.h -AC_CHECK_TYPES([struct if_dqinfo],[],[],[#include ]) -AC_CHECK_TYPES([struct if_dqblk],[],[],[#include ]) +# See note there re: __ASM_X86_64_PROCESSOR_H + +AC_CHECK_TYPES([struct if_dqinfo],[],[],[ +#define __ASM_X86_64_PROCESSOR_H +#include +]) + +AC_CHECK_TYPES([struct if_dqblk],[],[],[ +#define __ASM_X86_64_PROCESSOR_H +#include +]) # liblustre/llite_lib.h AC_CHECK_HEADERS([xtio.h file.h]) @@ -463,9 +514,11 @@ AC_DEFUN([LC_CONDITIONALS], AM_CONDITIONAL(EXTN, test x$enable_extN = xyes) AM_CONDITIONAL(LDISKFS, test x$enable_ldiskfs = xyes) AM_CONDITIONAL(USE_QUILT, test x$QUILT != xno) -AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests) AM_CONDITIONAL(LIBLUSTRE, test x$enable_liblustre = xyes) +AM_CONDITIONAL(LIBLUSTRE_TESTS, test x$enable_liblustre_tests = xyes) AM_CONDITIONAL(MPITESTS, test x$enable_mpitests = xyes, Build MPI Tests) +AM_CONDITIONAL(CLIENT, test x$enable_client = xyes) +AM_CONDITIONAL(SERVER, test x$enable_server = xyes) ]) # diff --git a/lustre/autoconf/lustre-version.ac b/lustre/autoconf/lustre-version.ac index 98c7811..b3d7ec7 100644 --- a/lustre/autoconf/lustre-version.ac +++ b/lustre/autoconf/lustre-version.ac @@ -1 +1 @@ -m4_define([LUSTRE_VERSION],[1.4.1.11]) +m4_define([LUSTRE_VERSION],[1.4.3.3]) diff --git a/lustre/include/liblustre.h b/lustre/include/liblustre.h index 42a7902..382021e 100644 --- a/lustre/include/liblustre.h +++ b/lustre/include/liblustre.h @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include @@ -76,6 +78,10 @@ typedef unsigned short umode_t; #endif +#ifndef CURRENT_SECONDS +# define CURRENT_SECONDS time(0) +#endif + /* This is because lprocfs_status.h gets included here indirectly. It would * be much better to just avoid lprocfs being included into liblustre entirely * but that requires more header surgery than I can handle right now. @@ -86,7 +92,7 @@ typedef unsigned short umode_t; /* always adopt 2.5 definitions */ #define KERNEL_VERSION(a,b,c) ((a)*100+(b)*10+c) -#define LINUX_VERSION_CODE (2*200+5*10+0) +#define LINUX_VERSION_CODE KERNEL_VERSION(2,5,0) static inline void inter_module_put(void *a) { @@ -162,35 +168,38 @@ typedef int (write_proc_t)(struct file *file, const char *buffer, #endif /* __LITTLE_ENDIAN */ /* bits ops */ -static __inline__ int set_bit(int nr,long * addr) + +/* a long can be more than 32 bits, so use BITS_PER_LONG + * to allow the compiler to adjust the bit shifting accordingly + */ + +/* test if bit nr is set in bitmap addr; returns previous value of bit nr */ +static __inline__ int set_bit(int nr, long * addr) { - int mask, retval; + long mask; - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *addr) != 0; - *addr |= mask; - return retval; + addr += nr / BITS_PER_LONG; + mask = 1UL << (nr & (BITS_PER_LONG - 1)); + nr = (mask & *addr) != 0; + *addr |= mask; + return nr; } +/* clear bit nr in bitmap addr; returns previous value of bit nr*/ static __inline__ int clear_bit(int nr, long * addr) { - int mask, retval; + long mask; - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - retval = (mask & *addr) != 0; - *addr &= ~mask; - return retval; + addr += nr / BITS_PER_LONG; + mask = 1UL << (nr & (BITS_PER_LONG - 1)); + nr = (mask & *addr) != 0; + *addr &= ~mask; + return nr; } static __inline__ int test_bit(int nr, long * addr) { - int mask; - - addr += nr >> 5; - mask = 1 << (nr & 0x1f); - return ((mask & *addr) != 0); + return ((1UL << (nr & (BITS_PER_LONG - 1))) & ((addr)[nr / BITS_PER_LONG])) != 0; } static __inline__ int ext2_set_bit(int nr, void *addr) @@ -312,14 +321,7 @@ static inline void spin_unlock_irqrestore(spinlock_t *a, unsigned long b) {} /* random */ -static inline void get_random_bytes(void *ptr, int size) -{ - int *p = (int *)ptr; - int i, count = size/sizeof(int); - - for (i = 0; i< count; i++) - *p++ = rand(); -} +void get_random_bytes(void *ptr, int size); /* memory */ @@ -375,6 +377,10 @@ static inline int kmem_cache_destroy(kmem_cache_t *a) /* struct page decl moved out from here into portals/include/libcfs/user-prim.h */ +/* 2.4 defines */ +#define PAGE_LIST_ENTRY list +#define PAGE_LIST(page) ((page)->list) + #define kmap(page) (page)->addr #define kunmap(a) do {} while (0) @@ -552,24 +558,28 @@ struct signal { int signal; }; -struct fs_struct { - int umask; -}; - struct task_struct { - struct fs_struct *fs; int state; struct signal pending; char comm[32]; int pid; int fsuid; int fsgid; + int max_groups; + int ngroups; + gid_t *groups; __u32 cap_effective; }; extern struct task_struct *current; - -#define in_group_p(a) 0 /* FIXME */ +int in_group_p(gid_t gid); +static inline int capable(int cap) +{ + if (current->cap_effective & (1 << cap)) + return 1; + else + return 0; +} #define set_current_state(foo) do { current->state = foo; } while (0) @@ -609,6 +619,7 @@ static inline int schedule_timeout(signed long t) } #define lock_kernel() do {} while (0) +#define unlock_kernel() do {} while (0) #define daemonize(l) do {} while (0) #define sigfillset(l) do {} while (0) #define recalc_sigpending(l) do {} while (0) @@ -682,6 +693,33 @@ typedef struct { volatile int counter; } atomic_t; #define unlikely(exp) (exp) #endif +/* FIXME sys/capability will finally included linux/fs.h thus + * cause numerous trouble on x86-64. as temporary solution for + * build broken at cary, we copy definition we need from capability.h + * FIXME + */ +struct _cap_struct; +typedef struct _cap_struct *cap_t; +typedef int cap_value_t; +typedef enum { + CAP_EFFECTIVE=0, + CAP_PERMITTED=1, + CAP_INHERITABLE=2 +} cap_flag_t; +typedef enum { + CAP_CLEAR=0, + CAP_SET=1 +} cap_flag_value_t; + +#define CAP_DAC_OVERRIDE 1 +#define CAP_DAC_READ_SEARCH 2 +#define CAP_FOWNER 3 +#define CAP_FSETID 4 +#define CAP_SYS_ADMIN 21 + +cap_t cap_get_proc(void); +int cap_get_flag(cap_t, cap_value_t, cap_flag_t, cap_flag_value_t *); + /* log related */ static inline int llog_init_commit_master(void) { return 0; } static inline int llog_cleanup_commit_master(int force) { return 0; } @@ -718,6 +756,62 @@ void *liblustre_register_wait_callback(int (*fn)(void *arg), void *arg); void liblustre_deregister_wait_callback(void *notifier); int liblustre_wait_event(int timeout); +/* flock related */ +struct nfs_lock_info { + __u32 state; + __u32 flags; + void *host; +}; + +struct file_lock { + struct file_lock *fl_next; /* singly linked list for this inode */ + struct list_head fl_link; /* doubly linked list of all locks */ + struct list_head fl_block; /* circular list of blocked processes */ + void *fl_owner; + unsigned int fl_pid; + wait_queue_head_t fl_wait; + struct file *fl_file; + unsigned char fl_flags; + unsigned char fl_type; + loff_t fl_start; + loff_t fl_end; + + void (*fl_notify)(struct file_lock *); /* unblock callback */ + void (*fl_insert)(struct file_lock *); /* lock insertion callback */ + void (*fl_remove)(struct file_lock *); /* lock removal callback */ + + void *fl_fasync; /* for lease break notifications */ + unsigned long fl_break_time; /* for nonblocking lease breaks */ + + union { + struct nfs_lock_info nfs_fl; + } fl_u; +}; + +#ifndef OFFSET_MAX +#define INT_LIMIT(x) (~((x)1 << (sizeof(x)*8 - 1))) +#define OFFSET_MAX INT_LIMIT(loff_t) +#endif + +/* XXX: defined in kernel */ +#define FL_POSIX 1 +#define FL_SLEEP 128 + +/* quota */ +#define QUOTA_OK 0 +#define NO_QUOTA 1 + +/* proc */ +#define proc_symlink(...) \ +({ \ + void *result = NULL; \ + result; \ +}) + +#ifndef ENOTSUPP +#define ENOTSUPP ENOTSUP +#endif + #include #include #include diff --git a/lustre/include/linux/lprocfs_status.h b/lustre/include/linux/lprocfs_status.h index 6c0adbd..5ef1d2e4 100644 --- a/lustre/include/linux/lprocfs_status.h +++ b/lustre/include/linux/lprocfs_status.h @@ -128,6 +128,7 @@ extern struct proc_dir_entry *proc_lustre_root; struct obd_device; struct file; +struct obd_histogram; #ifdef LPROCFS @@ -259,7 +260,6 @@ extern int lprocfs_write_u64_helper(const char *buffer, unsigned long count, __u64 *val); int lprocfs_obd_seq_create(struct obd_device *dev, char *name, mode_t mode, struct file_operations *seq_fops, void *data); -struct obd_histogram; void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value); void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value); void lprocfs_oh_clear(struct obd_histogram *oh); @@ -303,7 +303,7 @@ static inline void lprocfs_free_obd_stats(struct obd_device *obddev) static inline struct proc_dir_entry * lprocfs_register(const char *name, struct proc_dir_entry *parent, struct lprocfs_vars *list, void *data) { return NULL; } -#define LPROCFS_INIT_VARS(name, vclass, vinstance) do {} while (0) +#define LPROCFS_INIT_VARS(name, vclass, vinstance) #define lprocfs_init_vars(...) do {} while (0) static inline int lprocfs_add_vars(struct proc_dir_entry *root, struct lprocfs_vars *var, @@ -363,6 +363,14 @@ static inline int lprocfs_rd_filegroups(char *page, char **start, off_t off, int count, int *eof, void *data) { return 0; } static inline +void lprocfs_oh_tally(struct obd_histogram *oh, unsigned int value) {} +static inline +void lprocfs_oh_tally_log2(struct obd_histogram *oh, unsigned int value) {} +static inline +void lprocfs_oh_clear(struct obd_histogram *oh) {} +static inline +unsigned long lprocfs_oh_sum(struct obd_histogram *oh) { return 0; } +static inline int lprocfs_counter_read(char *page, char **start, off_t off, int count, int *eof, void *data) { return 0; } static inline diff --git a/lustre/include/linux/lustre_cfg.h b/lustre/include/linux/lustre_cfg.h index 5bc0b95..b9dee63 100644 --- a/lustre/include/linux/lustre_cfg.h +++ b/lustre/include/linux/lustre_cfg.h @@ -63,6 +63,7 @@ struct lustre_cfg { uint32_t lcfg_num; uint32_t lcfg_flags; uint64_t lcfg_nid; + uint32_t lcfg_nal; /* not used any more */ uint32_t lcfg_bufcount; uint32_t lcfg_buflens[0]; diff --git a/lustre/include/linux/lustre_compat25.h b/lustre/include/linux/lustre_compat25.h index 0de68b6..35a7cde 100644 --- a/lustre/include/linux/lustre_compat25.h +++ b/lustre/include/linux/lustre_compat25.h @@ -44,9 +44,8 @@ #endif /* XXX our code should be using the 2.6 calls, not the other way around */ -#define TryLockPage(page) TestSetPageLocked(page) -#define filemap_fdatasync(mapping) filemap_fdatawrite(mapping) -#define Page_Uptodate(page) PageUptodate(page) +#define TryLockPage(page) TestSetPageLocked(page) +#define Page_Uptodate(page) PageUptodate(page) #define ll_redirty_page(page) set_page_dirty(page) #define KDEVT_INIT(val) (val) @@ -64,8 +63,7 @@ #define ll_truncate_complete_page(page) \ truncate_complete_page(page->mapping, page) -#define ll_vfs_create(a,b,c,d) vfs_create(a,b,c,d) - +#define ll_vfs_create(a,b,c,d) vfs_create(a,b,c,d) #define ll_dev_t dev_t #define kdev_t dev_t #define to_kdev_t(dev) (dev) @@ -120,8 +118,8 @@ static inline int cleanup_group_info(void) #else /* 2.4.. */ -#ifdef HAVE_MM_INLINE -#include +#ifdef HAVE_MM_INLINE +#include #endif #define ll_vfs_create(a,b,c,d) vfs_create(a,b,c) @@ -236,8 +234,8 @@ static inline void cond_resched(void) #define __set_page_ll_data(page, llap) page->private = (unsigned long)llap #define __clear_page_ll_data(page) page->private = 0 #define PageWriteback(page) 0 -#define set_page_writeback(page) -#define end_page_writeback(page) +#define set_page_writeback(page) do {} while (0) +#define end_page_writeback(page) do {} while (0) static inline int mapping_mapped(struct address_space *mapping) { @@ -324,5 +322,9 @@ static inline int mapping_has_pages(struct address_space *mapping) #define grab_cache_page_nowait_gfp(x, y, z) grab_cache_page_nowait((x), (y)) #endif +#ifndef HAVE_FILEMAP_FDATAWRITE +#define filemap_fdatawrite(mapping) filemap_fdatasync(mapping) +#endif + #endif /* __KERNEL__ */ #endif /* _COMPAT25_H */ diff --git a/lustre/include/linux/lustre_debug.h b/lustre/include/linux/lustre_debug.h index 1ca2d89..e550272 100644 --- a/lustre/include/linux/lustre_debug.h +++ b/lustre/include/linux/lustre_debug.h @@ -42,10 +42,16 @@ do { if (offset > ASSERT_MAX_SIZE_MB << 20) { \ OP; \ }} while(0) +#ifdef __KERNEL__ #define LL_CDEBUG_PAGE(mask, page, fmt, arg...) \ CDEBUG(mask, "page %p map %p index %lu flags %lx count %u priv %0lx: "\ fmt, page, page->mapping, page->index, (long)page->flags, \ page_count(page), page->private, ## arg) +#else +#define LL_CDEBUG_PAGE(mask, page, fmt, arg...) \ + CDEBUG(mask, "page %p index %lu priv %0lx: "\ + fmt, page, page->index, page->private, ## arg) +#endif /* lib/debug.c */ int dump_lniobuf(struct niobuf_local *lnb); diff --git a/lustre/include/linux/lustre_dlm.h b/lustre/include/linux/lustre_dlm.h index 8686034..fcc9a74 100644 --- a/lustre/include/linux/lustre_dlm.h +++ b/lustre/include/linux/lustre_dlm.h @@ -68,6 +68,9 @@ typedef enum { #define LDLM_FL_WARN 0x008000 /* see ldlm_cli_cancel_unused */ #define LDLM_FL_DISCARD_DATA 0x010000 /* discard (no writeback) on cancel */ +#define LDLM_FL_NO_TIMEOUT 0x020000 /* Blocked by CW lock - wait + indefinitely */ + /* file & record locking */ #define LDLM_FL_BLOCK_NOWAIT 0x040000 // server told not to wait if blocked #define LDLM_FL_TEST_LOCK 0x080000 // return blocking lock @@ -110,6 +113,7 @@ typedef enum { #define LCK_COMPAT_CW (LCK_COMPAT_PW | LCK_CW) #define LCK_COMPAT_CR (LCK_COMPAT_CW | LCK_PR | LCK_PW) #define LCK_COMPAT_NL (LCK_COMPAT_CR | LCK_EX) +#define LCK_COMPAT_GROUP (LCK_GROUP | LCK_NL) static ldlm_mode_t lck_compat_array[] = { [LCK_EX] LCK_COMPAT_EX, @@ -117,12 +121,13 @@ static ldlm_mode_t lck_compat_array[] = { [LCK_PR] LCK_COMPAT_PR, [LCK_CW] LCK_COMPAT_CW, [LCK_CR] LCK_COMPAT_CR, - [LCK_NL] LCK_COMPAT_NL + [LCK_NL] LCK_COMPAT_NL, + [LCK_GROUP] LCK_COMPAT_GROUP }; static inline void lockmode_verify(ldlm_mode_t mode) { - LASSERT(mode >= LCK_EX && mode <= LCK_NL); + LASSERT(mode > LCK_MINMODE && mode < LCK_MAXMODE); } static inline int lockmode_compat(ldlm_mode_t exist, ldlm_mode_t new) @@ -168,9 +173,6 @@ struct ldlm_namespace { struct list_head ns_root_list; /* all root resources in ns */ struct lustre_lock ns_lock; /* protects hash, refcount, list */ struct list_head ns_list_chain; /* position in global NS list */ - /* - struct proc_dir_entry *ns_proc_dir; - */ struct list_head ns_unused_list; /* all root resources in ns */ int ns_nr_unused; @@ -488,7 +490,11 @@ struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 local); int ldlm_namespace_cleanup(struct ldlm_namespace *ns, int flags); int ldlm_namespace_free(struct ldlm_namespace *ns, int force); int ldlm_proc_setup(void); +#ifdef LPROCFS void ldlm_proc_cleanup(void); +#else +static inline void ldlm_proc_cleanup(void) {} +#endif /* resource.c - internal */ struct ldlm_resource *ldlm_resource_get(struct ldlm_namespace *ns, diff --git a/lustre/include/linux/lustre_export.h b/lustre/include/linux/lustre_export.h index 5136d66..012f3c8 100644 --- a/lustre/include/linux/lustre_export.h +++ b/lustre/include/linux/lustre_export.h @@ -12,10 +12,10 @@ struct mds_client_data; struct mds_export_data { struct list_head med_open_head; - spinlock_t med_open_lock; + spinlock_t med_open_lock; /* lock med_open_head, mfd_list*/ struct mds_client_data *med_mcd; - loff_t med_off; - int med_idx; + loff_t med_lr_off; + int med_lr_idx; }; struct osc_creator { @@ -55,6 +55,7 @@ struct obd_export { atomic_t exp_refcount; struct obd_uuid exp_client_uuid; struct list_head exp_obd_chain; + struct list_head exp_obd_chain_timed; /* for ping evictor */ struct obd_device *exp_obd; struct obd_import *exp_imp_reverse; /* to make RPCs backwards */ struct ptlrpc_connection *exp_connection; @@ -67,6 +68,7 @@ struct obd_export { __u64 exp_connect_flags; int exp_flags; unsigned int exp_failed:1, + exp_disconnected:1, exp_replay_needed:1, exp_libclient:1; /* liblustre client? */ union { diff --git a/lustre/include/linux/lustre_fsfilt.h b/lustre/include/linux/lustre_fsfilt.h index ceb3a41..2cc850e 100644 --- a/lustre/include/linux/lustre_fsfilt.h +++ b/lustre/include/linux/lustre_fsfilt.h @@ -1,7 +1,7 @@ /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- * vim:expandtab:shiftwidth=8:tabstop=8: * - * Copyright (C) 2001 Cluster File Systems, Inc. + * Copyright (C) 2001-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -58,7 +58,16 @@ struct fsfilt_operations { int (* fs_set_md)(struct inode *inode, void *handle, void *md, int size); int (* fs_get_md)(struct inode *inode, void *md, int size); - /* this method is needed to make IO operation fsfilt nature depend. */ + /* + * this method is needed to make IO operation fsfilt nature depend. + * + * This operation maybe synchronous or asynchronous. + * + * Return convention: positive number of bytes written (synchronously) + * on success. Negative errno value on failure. Zero if asynchronous + * IO was submitted successfully. + * + */ int (* fs_send_bio)(int rw, struct inode *inode,struct kiobuf *bio); ssize_t (* fs_readpage)(struct file *file, char *buf, size_t count, loff_t *offset); @@ -82,7 +91,7 @@ struct fsfilt_operations { struct obd_quotactl *oqctl); int (* fs_quotactl)(struct super_block *sb, struct obd_quotactl *oqctl); - int (* fs_quotainfo)(struct lustre_quota_info *lqi, int type, + int (* fs_quotainfo)(struct lustre_quota_info *lqi, int type, int cmd); int (* fs_dquot)(struct lustre_dquot *dquot, int cmd); }; @@ -253,7 +262,7 @@ static inline int fsfilt_send_bio(int rw, struct obd_device *obd, struct inode *inode, void *bio) { LASSERTF(rw == OBD_BRW_WRITE || rw == OBD_BRW_READ, "%x\n", rw); - + if (rw == OBD_BRW_READ) return obd->obd_fsops->fs_send_bio(READ, inode, bio); return obd->obd_fsops->fs_send_bio(WRITE, inode, bio); @@ -301,27 +310,35 @@ static inline int fsfilt_quotacheck(struct obd_device *obd, struct super_block *sb, struct obd_quotactl *oqctl) { - return obd->obd_fsops->fs_quotacheck(sb, oqctl); + if (obd->obd_fsops->fs_quotacheck) + return obd->obd_fsops->fs_quotacheck(sb, oqctl); + return -ENOTSUPP; } static inline int fsfilt_quotactl(struct obd_device *obd, struct super_block *sb, struct obd_quotactl *oqctl) { - return obd->obd_fsops->fs_quotactl(sb, oqctl); + if (obd->obd_fsops->fs_quotactl) + return obd->obd_fsops->fs_quotactl(sb, oqctl); + return -ENOTSUPP; } static inline int fsfilt_quotainfo(struct obd_device *obd, struct lustre_quota_info *lqi, int type, int cmd) { - return obd->obd_fsops->fs_quotainfo(lqi, type, cmd); + if (obd->obd_fsops->fs_quotainfo) + return obd->obd_fsops->fs_quotainfo(lqi, type, cmd); + return -ENOTSUPP; } static inline int fsfilt_dquot(struct obd_device *obd, struct lustre_dquot *dquot, int cmd) { - return obd->obd_fsops->fs_dquot(dquot, cmd); + if (obd->obd_fsops->fs_dquot) + return obd->obd_fsops->fs_dquot(dquot, cmd); + return -ENOTSUPP; } static inline int fsfilt_map_inode_pages(struct obd_device *obd, diff --git a/lustre/include/linux/lustre_ha.h b/lustre/include/linux/lustre_ha.h index 8b7bef4..5083b94 100644 --- a/lustre/include/linux/lustre_ha.h +++ b/lustre/include/linux/lustre_ha.h @@ -23,6 +23,5 @@ void ptlrpc_activate_import(struct obd_import *imp); void ptlrpc_deactivate_import(struct obd_import *imp); void ptlrpc_invalidate_import(struct obd_import *imp); void ptlrpc_fail_import(struct obd_import *imp, int generation); -void ptlrpc_fail_export(struct obd_export *exp); #endif diff --git a/lustre/include/linux/lustre_idl.h b/lustre/include/linux/lustre_idl.h index 9c6939c..9e676ab 100644 --- a/lustre/include/linux/lustre_idl.h +++ b/lustre/include/linux/lustre_idl.h @@ -197,6 +197,7 @@ static inline void lustre_msg_set_op_flags(struct lustre_msg *msg, int flags) //#define MSG_CONNECT_PEER 0x8 #define MSG_CONNECT_LIBCLIENT 0x10 #define MSG_CONNECT_INITIAL 0x20 +#define MSG_CONNECT_ASYNC 0x40 /* Connect flags */ @@ -369,11 +370,11 @@ struct lov_mds_md_v1 { /* LOV EA mds/wire data (little-endian) */ #define OBD_MD_MDS (0x100000000ULL) /* where an inode lives on */ #define OBD_MD_REINT (0x200000000ULL) /* reintegrate oa */ -#define OBD_MD_FLNOTOBD (~(OBD_MD_FLBLOCKS | OBD_MD_LINKNAME|\ - OBD_MD_FLEASIZE | OBD_MD_FLHANDLE | OBD_MD_FLCKSUM|\ - OBD_MD_FLQOS | OBD_MD_FLOSCOPQ | OBD_MD_FLCOOKIE|\ - OBD_MD_FLUSRQUOTA | OBD_MD_FLGRPQUOTA)) - +#define OBD_MD_FLGETATTR (OBD_MD_FLID | OBD_MD_FLATIME | OBD_MD_FLMTIME | \ + OBD_MD_FLCTIME | OBD_MD_FLSIZE | OBD_MD_FLBLKSZ | \ + OBD_MD_FLMODE | OBD_MD_FLTYPE | OBD_MD_FLUID | \ + OBD_MD_FLGID | OBD_MD_FLFLAGS | OBD_MD_FLNLINK | \ + OBD_MD_FLGENER | OBD_MD_FLRDEV | OBD_MD_FLGROUP) static inline struct lustre_handle *obdo_handle(struct obdo *oa) { @@ -499,6 +500,8 @@ typedef enum { REINT_UNLINK = 4, REINT_RENAME = 5, REINT_OPEN = 6, +// REINT_CLOSE = 7, +// REINT_WRITE = 8, REINT_MAX } mds_reint_t; @@ -731,7 +734,8 @@ extern void lustre_swab_mds_rec_rename (struct mds_rec_rename *rn); * LOV data structures */ -#define LOV_MIN_STRIPE_SIZE 65536UL /* maximum PAGE_SIZE (ia64), power of 2 */ +#define LOV_MIN_STRIPE_SIZE 65536 /* maximum PAGE_SIZE (ia64), power of 2 */ +#define LOV_MAX_STRIPE_COUNT 160 /* until bug 4424 is fixed */ #define LOV_MAX_UUID_BUFFER_SIZE 8192 /* The size of the buffer the lov/mdc reserves for the @@ -782,18 +786,22 @@ extern void lustre_swab_ldlm_res_id (struct ldlm_res_id *id); /* lock types */ typedef enum { + LCK_MINMODE = 0, LCK_EX = 1, LCK_PW = 2, LCK_PR = 4, LCK_CW = 8, LCK_CR = 16, - LCK_NL = 32 + LCK_NL = 32, + LCK_GROUP = 64, + LCK_MAXMODE } ldlm_mode_t; typedef enum { LDLM_PLAIN = 10, LDLM_EXTENT = 11, LDLM_FLOCK = 12, +// LDLM_IBITS = 13, LDLM_MAX_TYPE } ldlm_type_t; @@ -802,6 +810,7 @@ typedef enum { struct ldlm_extent { __u64 start; __u64 end; + __u64 gid; }; struct ldlm_flock { @@ -961,7 +970,7 @@ typedef enum { MDS_UNLINK_REC = LLOG_OP_MAGIC | 0x10000 | (MDS_REINT << 8) | REINT_UNLINK, MDS_SETATTR_REC = LLOG_OP_MAGIC | 0x10000 | (MDS_REINT << 8) | REINT_SETATTR, OBD_CFG_REC = LLOG_OP_MAGIC | 0x20000, -// unused = LLOG_OP_MAGIC | 0x30000, + PTL_CFG_REC = LLOG_OP_MAGIC | 0x30000, /* obsolete */ LLOG_GEN_REC = LLOG_OP_MAGIC | 0x40000, LLOG_HDR_MAGIC = LLOG_OP_MAGIC | 0x45539, LLOG_LOGID_MAGIC = LLOG_OP_MAGIC | 0x4553b, @@ -1139,10 +1148,10 @@ static inline struct ll_fid *obdo_fid(struct obdo *oa) /* qutoa */ struct qunit_data { - __u32 qd_id; - __u32 qd_type; - __u32 qd_count; - __u32 qd_isblk; /* indicating if it's block quota */ + __u32 qd_id; + __u32 qd_type; + __u32 qd_count; + __u32 qd_isblk; /* indicating if it's block quota */ }; extern void lustre_swab_qdata(struct qunit_data *d); diff --git a/lustre/include/linux/lustre_lib.h b/lustre/include/linux/lustre_lib.h index 2bc042c..b2baf7e 100644 --- a/lustre/include/linux/lustre_lib.h +++ b/lustre/include/linux/lustre_lib.h @@ -35,9 +35,7 @@ # include # include #endif -#include -#include -#include /* XXX just for LASSERT! */ +#include #include #include @@ -54,15 +52,28 @@ #endif #ifndef LPU64 -/* x86_64 has 64bit longs and defines u64 as long long */ -#if BITS_PER_LONG > 32 && !defined(__x86_64__) -#define LPU64 "%lu" -#define LPD64 "%ld" -#define LPX64 "%#lx" -#else -#define LPU64 "%Lu" -#define LPD64 "%Ld" -#define LPX64 "%#Lx" +/* x86_64 defines __u64 as "long" in userspace, but "long long" in the kernel */ +#if defined(__x86_64__) && defined(__KERNEL__) +# define LPU64 "%Lu" +# define LPD64 "%Ld" +# define LPX64 "%#Lx" +# define LPSZ "%lu" +# define LPSSZ "%ld" +#elif (BITS_PER_LONG == 32 || __WORDSIZE == 32) +# define LPU64 "%Lu" +# define LPD64 "%Ld" +# define LPX64 "%#Lx" +# define LPSZ "%u" +# define LPSSZ "%d" +#elif (BITS_PER_LONG == 64 || __WORDSIZE == 64) +# define LPU64 "%lu" +# define LPD64 "%ld" +# define LPX64 "%#lx" +# define LPSZ "%lu" +# define LPSSZ "%ld" +#endif +#ifndef LPU64 +# error "No word size defined" #endif #endif @@ -84,9 +95,14 @@ int target_handle_reconnect(struct lustre_handle *conn, struct obd_export *exp, int target_handle_ping(struct ptlrpc_request *req); void target_committed_to_req(struct ptlrpc_request *req); +#ifdef HAVE_QUOTA_SUPPORT /* quotacheck callback, dqacq/dqrel callback handler */ int target_handle_qc_callback(struct ptlrpc_request *req); int target_handle_dqacq_callback(struct ptlrpc_request *req); +#else +#define target_handle_dqacq_callback(req) ldlm_callback_reply(req, -ENOTSUPP) +#define target_handle_qc_callback(req) (0) +#endif void target_cancel_recovery_timer(struct obd_device *obd); @@ -194,59 +210,59 @@ static inline int obd_ioctl_packlen(struct obd_ioctl_data *data) static inline int obd_ioctl_is_invalid(struct obd_ioctl_data *data) { if (data->ioc_len > (1<<30)) { - printk("LustreError: OBD ioctl: ioc_len larger than 1<<30\n"); + CERROR("OBD ioctl: ioc_len larger than 1<<30\n"); return 1; } if (data->ioc_inllen1 > (1<<30)) { - printk("LustreError: OBD ioctl: ioc_inllen1 larger than 1<<30\n"); + CERROR("OBD ioctl: ioc_inllen1 larger than 1<<30\n"); return 1; } if (data->ioc_inllen2 > (1<<30)) { - printk("LustreError: OBD ioctl: ioc_inllen2 larger than 1<<30\n"); + CERROR("OBD ioctl: ioc_inllen2 larger than 1<<30\n"); return 1; } if (data->ioc_inllen3 > (1<<30)) { - printk("LustreError: OBD ioctl: ioc_inllen3 larger than 1<<30\n"); + CERROR("OBD ioctl: ioc_inllen3 larger than 1<<30\n"); return 1; } if (data->ioc_inllen4 > (1<<30)) { - printk("LustreError: OBD ioctl: ioc_inllen4 larger than 1<<30\n"); + CERROR("OBD ioctl: ioc_inllen4 larger than 1<<30\n"); return 1; } if (data->ioc_inlbuf1 && !data->ioc_inllen1) { - printk("LustreError: OBD ioctl: inlbuf1 pointer but 0 length\n"); + CERROR("OBD ioctl: inlbuf1 pointer but 0 length\n"); return 1; } if (data->ioc_inlbuf2 && !data->ioc_inllen2) { - printk("LustreError: OBD ioctl: inlbuf2 pointer but 0 length\n"); + CERROR("OBD ioctl: inlbuf2 pointer but 0 length\n"); return 1; } if (data->ioc_inlbuf3 && !data->ioc_inllen3) { - printk("LustreError: OBD ioctl: inlbuf3 pointer but 0 length\n"); + CERROR("OBD ioctl: inlbuf3 pointer but 0 length\n"); return 1; } if (data->ioc_inlbuf4 && !data->ioc_inllen4) { - printk("LustreError: OBD ioctl: inlbuf4 pointer but 0 length\n"); + CERROR("OBD ioctl: inlbuf4 pointer but 0 length\n"); return 1; } if (data->ioc_pbuf1 && !data->ioc_plen1) { - printk("LustreError: OBD ioctl: pbuf1 pointer but 0 length\n"); + CERROR("OBD ioctl: pbuf1 pointer but 0 length\n"); return 1; } if (data->ioc_pbuf2 && !data->ioc_plen2) { - printk("LustreError: OBD ioctl: pbuf2 pointer but 0 length\n"); + CERROR("OBD ioctl: pbuf2 pointer but 0 length\n"); return 1; } if (data->ioc_plen1 && !data->ioc_pbuf1) { - printk("LustreError: OBD ioctl: plen1 set but NULL pointer\n"); + CERROR("OBD ioctl: plen1 set but NULL pointer\n"); return 1; } if (data->ioc_plen2 && !data->ioc_pbuf2) { - printk("LustreError: OBD ioctl: plen2 set but NULL pointer\n"); + CERROR("OBD ioctl: plen2 set but NULL pointer\n"); return 1; } if (obd_ioctl_packlen(data) != data->ioc_len) { - printk("LustreError: OBD ioctl: packlen exceeds ioc_len (%d != %d)\n", + CERROR("OBD ioctl: packlen exceeds ioc_len (%d != %d)\n", obd_ioctl_packlen(data), data->ioc_len); return 1; } @@ -331,25 +347,23 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg) ENTRY; err = copy_from_user(&hdr, (void *)arg, sizeof(hdr)); - if ( err ) { - EXIT; - return err; - } + if (err) + RETURN(err); if (hdr.ioc_version != OBD_IOCTL_VERSION) { CERROR("Version mismatch kernel vs application\n"); - return -EINVAL; + RETURN(-EINVAL); } if (hdr.ioc_len > OBD_MAX_IOCTL_BUFFER) { CERROR("User buffer len %d exceeds %d max buffer\n", hdr.ioc_len, OBD_MAX_IOCTL_BUFFER); - return -EINVAL; + RETURN(-EINVAL); } if (hdr.ioc_len < sizeof(struct obd_ioctl_data)) { - printk("LustreError: OBD: user buffer too small for ioctl\n"); - return -EINVAL; + CERROR("user buffer too small for ioctl (%d)\n", hdr.ioc_len); + RETURN(-EINVAL); } /* XXX allocate this more intelligently, using kmalloc when @@ -364,14 +378,15 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg) data = (struct obd_ioctl_data *)*buf; err = copy_from_user(*buf, (void *)arg, hdr.ioc_len); - if ( err ) { - EXIT; - return err; + if (err) { + OBD_VFREE(*buf, hdr.ioc_len); + RETURN(err); } if (obd_ioctl_is_invalid(data)) { CERROR("ioctl not correctly formatted\n"); - return -EINVAL; + OBD_VFREE(*buf, hdr.ioc_len); + RETURN(-EINVAL); } if (data->ioc_inllen1) { @@ -393,8 +408,7 @@ static inline int obd_ioctl_getdata(char **buf, int *len, void *arg) data->ioc_inlbuf4 = &data->ioc_bulk[0] + offset; } - EXIT; - return 0; + RETURN(0); } static inline void obd_ioctl_freedata(char *buf, int len) @@ -430,6 +444,7 @@ static inline void obd_ioctl_freedata(char *buf, int len) #define OBD_IOC_BRW_WRITE _IOWR('f', 126, long) #define OBD_IOC_NAME2DEV _IOWR('f', 127, long) #define OBD_IOC_UUID2DEV _IOWR('f', 130, long) +#define OBD_IOC_GETNAME _IOR ('f', 131, long) #define OBD_IOC_LOV_GET_CONFIG _IOWR('f', 132, long) #define OBD_IOC_CLIENT_RECOVER _IOW ('f', 133, long) @@ -482,26 +497,8 @@ static inline void obd_ioctl_freedata(char *buf, int len) * we define this to be 2T - 4k, which is the ext3 maxbytes. */ #define LUSTRE_STRIPE_MAXBYTES 0x1fffffff000ULL -#define CHECKSUM_CHUNK 4096 -#define CHECKSUM_BULK 0 #define POISON_BULK 0 -#if CHECKSUM_BULK -static inline void ost_checksum(obd_count *cksum,int *psum, void *addr, int len) -{ - unsigned char *ptr = (unsigned char *)addr; - int sum = 0; - - /* very stupid, but means I don't have to think about byte order */ - while (len-- > 0) - sum += *ptr++; - - *cksum = (*cksum << 2) + sum; - if (psum) - *psum = sum; -} -#endif - static inline int ll_insecure_random_int(void) { struct timeval t; @@ -675,13 +672,29 @@ do { \ #else /* !__KERNEL__ */ #define __l_wait_event(wq, condition, info, ret, excl) \ do { \ + long timeout = info->lwi_timeout, elapse, last = 0; \ int __timed_out = 0; \ \ + if (info->lwi_timeout == 0) \ + timeout = 1000000000; \ + else \ + last = time(NULL); \ + \ for (;;) { \ if (condition) \ break; \ - if (liblustre_wait_event(info->lwi_timeout)) \ + if (liblustre_wait_event(timeout)) { \ + if (timeout == 0 || info->lwi_timeout == 0) \ + continue; \ + elapse = time(NULL) - last; \ + if (elapse) { \ + last += elapse; \ + timeout -= elapse; \ + if (timeout < 0) \ + timeout = 0; \ + } \ continue; \ + } \ if (info->lwi_timeout && !__timed_out) { \ __timed_out = 1; \ if (info->lwi_on_timeout == NULL || \ diff --git a/lustre/include/linux/lustre_lite.h b/lustre/include/linux/lustre_lite.h index c5c045d..094eafe 100644 --- a/lustre/include/linux/lustre_lite.h +++ b/lustre/include/linux/lustre_lite.h @@ -69,6 +69,11 @@ enum { #include #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 #endif diff --git a/lustre/include/linux/lustre_mds.h b/lustre/include/linux/lustre_mds.h index b6d30be..b3b23e8 100644 --- a/lustre/include/linux/lustre_mds.h +++ b/lustre/include/linux/lustre_mds.h @@ -68,19 +68,19 @@ struct mds_update_record { int ur_cookielen; struct llog_cookie *ur_logcookies; struct iattr ur_iattr; - struct obd_ucred ur_uc; + struct lvfs_ucred ur_uc; __u64 ur_rdev; __u64 ur_time; __u32 ur_mode; __u32 ur_flags; }; -#define ur_fsuid ur_uc.ouc_fsuid -#define ur_fsgid ur_uc.ouc_fsgid -#define ur_cap ur_uc.ouc_cap -#define ur_suppgid1 ur_uc.ouc_suppgid1 -#define ur_suppgid2 ur_uc.ouc_suppgid2 -#define ur_umask ur_uc.ouc_umask +#define ur_fsuid ur_uc.luc_fsuid +#define ur_fsgid ur_uc.luc_fsgid +#define ur_cap ur_uc.luc_cap +#define ur_suppgid1 ur_uc.luc_suppgid1 +#define ur_suppgid2 ur_uc.luc_suppgid2 +#define ur_umask ur_uc.luc_umask #define MDS_LR_SERVER_SIZE 512 @@ -131,13 +131,13 @@ struct mds_client_data { struct mds_file_data { struct portals_handle mfd_handle; /* must be first */ atomic_t mfd_refcount; - struct list_head mfd_list; + struct list_head mfd_list; /* protected by med_open_lock */ __u64 mfd_xid; int mfd_mode; struct dentry *mfd_dentry; }; -/* mds/mds_reint.c */ +/* mds/mds_reint.c */ int mds_reint_rec(struct mds_update_record *r, int offset, struct ptlrpc_request *req, struct lustre_handle *); diff --git a/lustre/include/linux/lustre_net.h b/lustre/include/linux/lustre_net.h index 4445a57..5003d73 100644 --- a/lustre/include/linux/lustre_net.h +++ b/lustre/include/linux/lustre_net.h @@ -71,6 +71,7 @@ /* ...and make consistent... */ +#ifdef __KERNEL__ #if (PTLRPC_MAX_BRW_SIZE > PTLRPC_MAX_BRW_PAGES * PAGE_SIZE) # undef PTLRPC_MAX_BRW_SIZE # define PTLRPC_MAX_BRW_SIZE (PTLRPC_MAX_BRW_PAGES * PAGE_SIZE) @@ -82,10 +83,16 @@ #if ((PTLRPC_MAX_BRW_PAGES & (PTLRPC_MAX_BRW_PAGES - 1)) != 0) #error "PTLRPC_MAX_BRW_PAGES isn't a power of two" #endif +#else /* !__KERNEL__ */ +/* PAGE_SIZE isn't a constant, can't use CPP on it. We assume that the + * limit is on the number of pages for large pages, which is currently true. */ +# undef PTLRPC_MAX_BRW_PAGES +# define PTLRPC_MAX_BRW_PAGES (PTLRPC_MAX_BRW_SIZE / PAGE_SIZE) +#endif /* __KERNEL__ */ /* Size over which to OBD_VMALLOC() rather than OBD_ALLOC() service request * buffers */ -#define SVC_BUF_VMALLOC_THRESHOLD (2*PAGE_SIZE) +#define SVC_BUF_VMALLOC_THRESHOLD (2 * PAGE_SIZE) /* The following constants determine how memory is used to buffer incoming * service requests. @@ -267,6 +274,14 @@ struct ptlrpc_reply_state { struct lustre_msg rs_msg; }; +enum rq_phase { + RQ_PHASE_NEW = 0xebc0de00, + RQ_PHASE_RPC = 0xebc0de01, + RQ_PHASE_BULK = 0xebc0de02, + RQ_PHASE_INTERPRET = 0xebc0de03, + RQ_PHASE_COMPLETE = 0xebc0de04, +}; + struct ptlrpc_request { int rq_type; /* one of PTL_RPC_MSG_* */ struct list_head rq_list; @@ -279,7 +294,7 @@ struct ptlrpc_request { rq_timedout:1, rq_resend:1, rq_restart:1, rq_replay:1, rq_no_resend:1, rq_waiting:1, rq_receiving_reply:1, rq_no_delay:1, rq_net_err:1; - int rq_phase; + enum rq_phase rq_phase; /* one of RQ_PHASE_* */ atomic_t rq_refcount; /* client-side refcount for SENT race */ int rq_request_portal; /* XXX FIXME bug 249 */ @@ -313,7 +328,10 @@ struct ptlrpc_request { struct timeval rq_arrival_time; /* request arrival time */ struct ptlrpc_reply_state *rq_reply_state; /* separated reply state */ struct ptlrpc_request_buffer_desc *rq_rqbd; /* incoming request buffer*/ - +#if CRAY_PORTALS + ptl_uid_t rq_uid; /* peer uid, used in MDS only */ +#endif + /* client-only incoming reply */ ptl_handle_md_t rq_reply_md_h; wait_queue_head_t rq_reply_waitq; @@ -338,13 +356,6 @@ struct ptlrpc_request { void *rq_ptlrpcd_data; }; - -#define RQ_PHASE_NEW 0xebc0de00 -#define RQ_PHASE_RPC 0xebc0de01 -#define RQ_PHASE_BULK 0xebc0de02 -#define RQ_PHASE_INTERPRET 0xebc0de03 -#define RQ_PHASE_COMPLETE 0xebc0de04 - static inline const char * ptlrpc_rqphase2str(struct ptlrpc_request *req) { @@ -378,9 +389,8 @@ ptlrpc_rqphase2str(struct ptlrpc_request *req) #define REQ_FLAGS_FMT "%s:%s%s%s%s%s%s%s%s%s" -#define DEBUG_REQ(level, req, fmt, args...) \ -do { \ -CDEBUG(level, "@@@ " fmt \ +#define __DEBUG_REQ(CDEB_TYPE, level, req, fmt, args...) \ +CDEB_TYPE(level, "@@@ " fmt \ " req@%p x"LPD64"/t"LPD64" o%d->%s@%s:%d lens %d/%d ref %d fl " \ REQ_FLAGS_FMT"/%x/%x rc %d/%d\n" , ## args, req, req->rq_xid, \ req->rq_transno, \ @@ -395,7 +405,15 @@ CDEBUG(level, "@@@ " fmt \ DEBUG_REQ_FLAGS(req), \ req->rq_reqmsg ? req->rq_reqmsg->flags : 0, \ req->rq_repmsg ? req->rq_repmsg->flags : 0, \ - req->rq_status, req->rq_repmsg ? req->rq_repmsg->status : 0); \ + req->rq_status, req->rq_repmsg ? req->rq_repmsg->status : 0) + +/* for most callers (level is a constant) this is resolved at compile time */ +#define DEBUG_REQ(level, req, fmt, args...) \ +do { \ + if ((level) & (D_ERROR | D_WARNING)) \ + __DEBUG_REQ(CDEBUG_LIMIT, level, req, fmt, ## args); \ + else \ + __DEBUG_REQ(CDEBUG, level, req, fmt, ## args); \ } while (0) struct ptlrpc_bulk_page { @@ -718,12 +736,12 @@ int ptlrpcd_addref(void); void ptlrpcd_decref(void); /* ptlrpc/lproc_ptlrpc.c */ -#ifdef __KERNEL__ -void ptlrpc_lprocfs_register_obd(struct obd_device *obddev); -void ptlrpc_lprocfs_unregister_obd(struct obd_device *obddev); +#ifdef LPROCFS +void ptlrpc_lprocfs_register_obd(struct obd_device *obd); +void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd); #else -#define ptlrpc_lprocfs_register_obd(param...) do{}while(0) -#define ptlrpc_lprocfs_unregister_obd(param...) do{}while(0) +static inline void ptlrpc_lprocfs_register_obd(struct obd_device *obd) {} +static inline void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd) {} #endif /* ptlrpc/llog_server.c */ diff --git a/lustre/include/linux/lustre_quota.h b/lustre/include/linux/lustre_quota.h index 85fa3a2..6e0355a 100644 --- a/lustre/include/linux/lustre_quota.h +++ b/lustre/include/linux/lustre_quota.h @@ -4,80 +4,24 @@ #ifndef _LUSTRE_QUOTA_H #define _LUSTRE_QUOTA_H -#include -#include +#ifdef __KERNEL__ +# include +#endif #include -/* 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 +#else -/* structures to access admin quotafile */ struct lustre_mem_dqinfo { - unsigned int dqi_bgrace; - unsigned int dqi_igrace; - unsigned long dqi_flags; - unsigned int dqi_blocks; - unsigned int dqi_free_blk; - unsigned int dqi_free_entry; }; struct lustre_quota_info { - struct semaphore qi_sem; - struct file *qi_files[MAXQUOTAS]; - struct lustre_mem_dqinfo qi_info[MAXQUOTAS]; }; -#ifdef __KERNEL__ struct lustre_dquot { - struct list_head dq_hash; - struct list_head dq_unused; - - /* this semaphore is unused until we implement wb dquot cache */ - struct semaphore dq_sem; - atomic_t dq_refcnt; - - struct lustre_quota_info *dq_info; - loff_t dq_off; - unsigned int dq_id; - int dq_type; - unsigned long dq_flags; - struct mem_dqblk dq_dqb; }; -#endif -#define QFILE_CHK 1 -#define QFILE_RD_INFO 2 -#define QFILE_WR_INFO 3 -#define QFILE_INIT_INFO 4 -#define QFILE_RD_DQUOT 5 -#define QFILE_WR_DQUOT 6 - -/* admin quotafile operations */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) -int lustre_check_quota_file(struct lustre_quota_info *lqi, int type); -int lustre_read_quota_info(struct lustre_quota_info *lqi, int type); -int lustre_write_quota_info(struct lustre_quota_info *lqi, int type); -#ifdef __KERNEL__ -int lustre_read_dquot(struct lustre_dquot *dquot); -int lustre_commit_dquot(struct lustre_dquot *dquot); -#endif -int lustre_init_quota_info(struct lustre_quota_info *lqi, int type); - -#else - -#ifndef DQ_FAKE_B -#define DQ_FAKE_B 6 -#endif static inline int lustre_check_quota_file(struct lustre_quota_info *lqi, int type) @@ -109,42 +53,19 @@ static inline int lustre_init_quota_info(struct lustre_quota_info *lqi, { return 0; } -#endif /* KERNEL_VERSION(2,5,0) */ -/* quota context structures */ struct obd_device; + typedef int (*dqacq_handler_t) (struct obd_device * obd, struct qunit_data * qd, int opc); struct lustre_quota_ctxt { - struct super_block *lqc_sb; - struct obd_import *lqc_import; - dqacq_handler_t lqc_handler; - unsigned long lqc_flags; - unsigned long lqc_iunit_sz; - unsigned long lqc_itune_sz; - unsigned long lqc_bunit_sz; - unsigned long lqc_btune_sz; }; struct lustre_qunit { - struct list_head lq_hash; - atomic_t lq_refcnt; - struct lustre_quota_ctxt *lq_ctxt; - struct qunit_data lq_data; - unsigned int lq_opc; - struct list_head lq_waiters; }; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) -int qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb, - dqacq_handler_t handler); -void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force); -int qctxt_adjust_qunit(struct obd_device *obd, struct lustre_quota_ctxt *qctxt, - uid_t uid, gid_t gid, __u32 isblk); -int qctxt_wait_on_dqacq(struct obd_device *obd, struct lustre_quota_ctxt *qctxt, - uid_t uid, gid_t gid, __u32 isblk); -#else +struct super_block; static inline int qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb, dqacq_handler_t handler) { @@ -166,18 +87,11 @@ static inline int qctxt_wait_on_dqacq(struct obd_device *obd, { return 0; } -#endif /* KERNEL_VERSION(2,5,0) */ - -/* quota check & quotactl */ -#define LUSTRE_ADMIN_QUOTAFILES {\ - "admin_quotafile.usr", /* user admin quotafile */\ - "admin_quotafile.grp" /* group admin quotafile */\ -} struct quotacheck_info { - struct completion qi_starting; - struct obd_export *qi_exp; - struct obd_quotactl qi_oqctl; }; +#define LL_DQUOT_OFF(sb) do {} while(0) + +#endif /*!HAVE_QUOTA_SUPPORT */ #endif /* _LUSTRE_QUOTA_H */ diff --git a/lustre/include/linux/lvfs.h b/lustre/include/linux/lvfs.h index e645441..2862b8d 100644 --- a/lustre/include/linux/lvfs.h +++ b/lustre/include/linux/lvfs.h @@ -37,13 +37,13 @@ #endif /* simple.c */ -struct obd_ucred { - __u32 ouc_fsuid; - __u32 ouc_fsgid; - __u32 ouc_cap; - __u32 ouc_suppgid1; - __u32 ouc_suppgid2; - __u32 ouc_umask; +struct lvfs_ucred { + __u32 luc_fsuid; + __u32 luc_fsgid; + __u32 luc_cap; + __u32 luc_suppgid1; + __u32 luc_suppgid2; + __u32 luc_umask; }; struct lvfs_callback_ops { @@ -52,11 +52,11 @@ struct lvfs_callback_ops { #define OBD_RUN_CTXT_MAGIC 0xC0FFEEAA #define OBD_CTXT_DEBUG /* development-only debugging */ -struct obd_run_ctxt { +struct lvfs_run_ctxt { struct vfsmount *pwdmnt; struct dentry *pwd; mm_segment_t fs; - struct obd_ucred ouc; + struct lvfs_ucred luc; int ngroups; struct lvfs_callback_ops cb_ops; #ifdef OBD_CTXT_DEBUG @@ -71,12 +71,12 @@ struct obd_run_ctxt { #endif /* lvfs_common.c */ -struct dentry *lvfs_fid2dentry(struct obd_run_ctxt *, __u64, __u32, __u64 ,void *data); +struct dentry *lvfs_fid2dentry(struct lvfs_run_ctxt *, __u64, __u32, __u64 ,void *data); -void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, - struct obd_ucred *cred); -void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx, - struct obd_ucred *cred); +void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx, + struct lvfs_ucred *cred); +void pop_ctxt(struct lvfs_run_ctxt *saved, struct lvfs_run_ctxt *new_ctx, + struct lvfs_ucred *cred); #ifdef __KERNEL__ diff --git a/lustre/include/linux/lvfs_linux.h b/lustre/include/linux/lvfs_linux.h index cf73d47..7c31b31 100644 --- a/lustre/include/linux/lvfs_linux.h +++ b/lustre/include/linux/lvfs_linux.h @@ -17,8 +17,8 @@ #define l_filp_open filp_open -struct obd_run_ctxt; -struct l_file *l_dentry_open(struct obd_run_ctxt *, struct l_dentry *, +struct lvfs_run_ctxt; +struct l_file *l_dentry_open(struct lvfs_run_ctxt *, struct l_dentry *, int flags); struct l_linux_dirent { @@ -32,4 +32,23 @@ struct l_readdir_callback { struct list_head *lrc_list; }; +# if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) +# define BDEVNAME_DECLARE_STORAGE(foo) char foo[BDEVNAME_SIZE] +# define ll_bdevname(SB, STORAGE) __bdevname(kdev_t_to_nr(SB->s_dev), STORAGE) +# define lvfs_sbdev(SB) ((SB)->s_bdev) +# define lvfs_sbdev_type struct block_device * + int fsync_bdev(struct block_device *); +# define lvfs_sbdev_sync fsync_bdev +# else +# define BDEVNAME_DECLARE_STORAGE(foo) char __unused_##foo +# define ll_bdevname(SB,STORAGE) ((void)__unused_##STORAGE,bdevname(lvfs_sbdev(SB))) +# define lvfs_sbdev(SB) (kdev_t_to_nr((SB)->s_dev)) +# define lvfs_sbdev_type kdev_t +# define lvfs_sbdev_sync fsync_dev +# endif + +void lvfs_set_rdonly(lvfs_sbdev_type dev); +int lvfs_check_rdonly(lvfs_sbdev_type dev); +void lvfs_clear_rdonly(lvfs_sbdev_type dev); + #endif diff --git a/lustre/include/linux/obd.h b/lustre/include/linux/obd.h index 4dca21b..c2b72b3 100644 --- a/lustre/include/linux/obd.h +++ b/lustre/include/linux/obd.h @@ -130,7 +130,7 @@ struct obd_async_page_ops { int (*ap_refresh_count)(void *data, int cmd); void (*ap_fill_obdo)(void *data, int cmd, struct obdo *oa); void (*ap_completion)(void *data, int cmd, struct obdo *oa, int rc); - void (*ap_get_ucred)(void *data, struct obd_ucred *ouc); + void (*ap_get_ucred)(void *data, struct lvfs_ucred *ouc); }; /* the `oig' is passed down from a caller of obd rw methods. the callee @@ -291,6 +291,9 @@ struct client_obd { struct mdc_rpc_lock *cl_setattr_lock; struct osc_creator cl_oscc; + /* Flags section */ + unsigned int cl_checksum:1; /* debug checksums */ + /* also protected by the poorly named _loi_list_lock lock above */ struct osc_async_rc cl_ar; @@ -521,10 +524,12 @@ struct obd_device { spinlock_t obd_osfs_lock; struct obd_statfs obd_osfs; unsigned long obd_osfs_age; /* jiffies */ - struct obd_run_ctxt obd_ctxt; + struct lvfs_run_ctxt obd_lvfs_ctxt; struct llog_ctxt *obd_llog_ctxt[LLOG_MAX_CTXTS]; struct obd_device *obd_observer; struct obd_export *obd_self_export; + struct list_head obd_exports_timed; /* for ping evictor */ + time_t obd_eviction_timer; /* for ping evictor */ /* XXX encapsulate all this recovery data into one struct */ svc_handler_t obd_recovery_handler; @@ -581,7 +586,7 @@ struct obd_ops { int (*o_attach)(struct obd_device *dev, obd_count len, void *data); int (*o_detach)(struct obd_device *dev); int (*o_setup) (struct obd_device *dev, obd_count len, void *data); - int (*o_precleanup)(struct obd_device *dev); + int (*o_precleanup)(struct obd_device *dev, int cleanup_stage); int (*o_cleanup)(struct obd_device *dev); int (*o_process_config)(struct obd_device *dev, obd_count len, void *data); diff --git a/lustre/include/linux/obd_class.h b/lustre/include/linux/obd_class.h index eaacac5..bdf2d27 100644 --- a/lustre/include/linux/obd_class.h +++ b/lustre/include/linux/obd_class.h @@ -44,7 +44,7 @@ #include /* OBD Device Declarations */ -#define MAX_OBD_DEVICES 256 +#define MAX_OBD_DEVICES 520 extern struct obd_device obd_dev[MAX_OBD_DEVICES]; extern spinlock_t obd_dev_lock; @@ -79,6 +79,17 @@ void oig_complete_one(struct obd_io_group *oig, struct oig_callback_context *occ, int rc); void oig_release(struct obd_io_group *oig); int oig_wait(struct obd_io_group *oig); +/* ping evictor */ +#ifdef __KERNEL__ +void ping_evictor_start(void); +void ping_evictor_stop(void); +#else +#define ping_evictor_start() do {} while (0) +#define ping_evictor_stop() do {} while (0) +#endif + + +char *obd_export_nid2str(struct obd_export *exp); /* config.c */ int class_process_config(struct lustre_cfg *lcfg); @@ -93,9 +104,9 @@ struct config_llog_instance { char * cfg_instance; struct obd_uuid cfg_uuid; }; -int class_config_parse_llog(struct llog_ctxt *ctxt, char *name, +int class_config_parse_llog(struct llog_ctxt *ctxt, char *name, struct config_llog_instance *cfg); -int class_config_dump_llog(struct llog_ctxt *ctxt, char *name, +int class_config_dump_llog(struct llog_ctxt *ctxt, char *name, struct config_llog_instance *cfg); struct lustre_profile { @@ -129,6 +140,7 @@ do { \ void __class_export_put(struct obd_export *); struct obd_export *class_new_export(struct obd_device *obddev); void class_unlink_export(struct obd_export *exp); +void class_update_export_timer(struct obd_export *exp, time_t extra_delay); struct obd_import *class_import_get(struct obd_import *); void class_import_put(struct obd_import *); @@ -140,6 +152,7 @@ void class_put_type(struct obd_type *type); int class_connect(struct lustre_handle *conn, struct obd_device *obd, struct obd_uuid *cluuid); int class_disconnect(struct obd_export *exp); +void class_fail_export(struct obd_export *exp); void class_disconnect_exports(struct obd_device *obddev); void class_disconnect_stale_exports(struct obd_device *obddev); /* generic operations shared by various OBD types */ @@ -158,6 +171,7 @@ void obdo_cpy_md(struct obdo *dst, struct obdo *src, obd_flag valid); int obdo_cmp_md(struct obdo *dst, struct obdo *src, obd_flag compare); void obdo_to_ioobj(struct obdo *oa, struct obd_ioobj *ioobj); + #define OBT(dev) (dev)->obd_type #define OBP(dev, op) (dev)->obd_type->typ_ops->o_ ## op #define CTXTP(ctxt, op) (ctxt)->loc_logops->lop_##op @@ -278,7 +292,7 @@ static inline int obd_setup(struct obd_device *obd, int datalen, void *data) RETURN(rc); } -static inline int obd_precleanup(struct obd_device *obd) +static inline int obd_precleanup(struct obd_device *obd, int cleanup_stage) { int rc; ENTRY; @@ -286,7 +300,7 @@ static inline int obd_precleanup(struct obd_device *obd) OBD_CHECK_OP(obd, precleanup, 0); OBD_COUNTER_INCREMENT(obd, precleanup); - rc = OBP(obd, precleanup)(obd); + rc = OBP(obd, precleanup)(obd, cleanup_stage); RETURN(rc); } @@ -294,8 +308,8 @@ static inline int obd_cleanup(struct obd_device *obd) { int rc; ENTRY; - - OBD_CHECK_DEV(obd); + + OBD_CHECK_DEV(obd); OBD_CHECK_OP(obd, cleanup, 0); OBD_COUNTER_INCREMENT(obd, cleanup); @@ -308,10 +322,10 @@ obd_process_config(struct obd_device *obd, int datalen, void *data) { int rc; ENTRY; - + OBD_CHECK_OP(obd, process_config, -EOPNOTSUPP); OBD_COUNTER_INCREMENT(obd, process_config); - + rc = OBP(obd, process_config)(obd, datalen, data); RETURN(rc); } @@ -468,17 +482,17 @@ static inline int obd_setattr(struct obd_export *exp, struct obdo *obdo, RETURN(rc); } -static inline int obd_setattr_async(struct obd_export *exp, +static inline int obd_setattr_async(struct obd_export *exp, struct obdo *obdo, struct lov_stripe_md *ea, struct obd_trans_info *oti) { int rc; ENTRY; - + EXP_CHECK_OP(exp, setattr_async); OBD_COUNTER_INCREMENT(exp->exp_obd, setattr_async); - + rc = OBP(exp->exp_obd, setattr_async)(exp, obdo, ea, oti); RETURN(rc); } @@ -564,7 +578,7 @@ obd_lvfs_fid2dentry(struct obd_export *exp, __u64 id_ino, __u32 gen, __u64 gr) { LASSERT(exp->exp_obd); - return lvfs_fid2dentry(&exp->exp_obd->obd_ctxt, id_ino, gen, gr, + return lvfs_fid2dentry(&exp->exp_obd->obd_lvfs_ctxt, id_ino, gen, gr, exp->exp_obd); } @@ -606,7 +620,7 @@ static inline int obd_statfs(struct obd_device *obd, struct obd_statfs *osfs, } static inline int obd_sync(struct obd_export *exp, struct obdo *oa, - struct lov_stripe_md *ea, obd_size start, + struct lov_stripe_md *ea, obd_size start, obd_size end) { int rc; @@ -675,11 +689,11 @@ static inline int obd_brw_async(int cmd, struct obd_export *exp, RETURN(rc); } -static inline int obd_prep_async_page(struct obd_export *exp, +static inline int obd_prep_async_page(struct obd_export *exp, struct lov_stripe_md *lsm, - struct lov_oinfo *loi, - struct page *page, obd_off offset, - struct obd_async_page_ops *ops, + struct lov_oinfo *loi, + struct page *page, obd_off offset, + struct obd_async_page_ops *ops, void *data, void **res) { int ret; @@ -722,16 +736,16 @@ static inline int obd_set_async_flags(struct obd_export *exp, OBD_CHECK_OP(exp->exp_obd, set_async_flags, -EOPNOTSUPP); OBD_COUNTER_INCREMENT(exp->exp_obd, set_async_flags); - rc = OBP(exp->exp_obd, set_async_flags)(exp, lsm, loi, cookie, + rc = OBP(exp->exp_obd, set_async_flags)(exp, lsm, loi, cookie, async_flags); RETURN(rc); } -static inline int obd_queue_group_io(struct obd_export *exp, - struct lov_stripe_md *lsm, - struct lov_oinfo *loi, - struct obd_io_group *oig, - void *cookie, int cmd, obd_off off, +static inline int obd_queue_group_io(struct obd_export *exp, + struct lov_stripe_md *lsm, + struct lov_oinfo *loi, + struct obd_io_group *oig, + void *cookie, int cmd, obd_off off, int count, obd_flag brw_flags, obd_flag async_flags) { @@ -742,14 +756,14 @@ static inline int obd_queue_group_io(struct obd_export *exp, OBD_COUNTER_INCREMENT(exp->exp_obd, queue_group_io); LASSERT(cmd & OBD_BRW_RWMASK); - rc = OBP(exp->exp_obd, queue_group_io)(exp, lsm, loi, oig, cookie, + rc = OBP(exp->exp_obd, queue_group_io)(exp, lsm, loi, oig, cookie, cmd, off, count, brw_flags, async_flags); RETURN(rc); } -static inline int obd_trigger_group_io(struct obd_export *exp, - struct lov_stripe_md *lsm, +static inline int obd_trigger_group_io(struct obd_export *exp, + struct lov_stripe_md *lsm, struct lov_oinfo *loi, struct obd_io_group *oig) { @@ -809,6 +823,11 @@ static inline int obd_commitrw(int cmd, struct obd_export *exp, struct obdo *oa, RETURN(rc); } +/* b1_4_bug5047 has changes to make this an obd_merge_lvb() method */ +__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms_only); +__u64 lov_merge_blocks(struct lov_stripe_md *lsm); +__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time); + static inline int obd_adjust_kms(struct obd_export *exp, struct lov_stripe_md *lsm, obd_off size, int shrink) @@ -870,7 +889,7 @@ static inline int obd_match(struct obd_export *exp, struct lov_stripe_md *ea, } static inline int obd_change_cbdata(struct obd_export *exp, - struct lov_stripe_md *lsm, + struct lov_stripe_md *lsm, ldlm_iterator_t it, void *data) { int rc; @@ -1010,7 +1029,7 @@ static inline int obd_quotacheck(struct obd_export *exp, rc = OBP(exp->exp_obd, quotacheck)(exp, oqctl); RETURN(rc); -} +} static inline int obd_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) @@ -1023,7 +1042,7 @@ static inline int obd_quotactl(struct obd_export *exp, rc = OBP(exp->exp_obd, quotactl)(exp, oqctl); RETURN(rc); -} +} static inline int obd_register_observer(struct obd_device *obd, diff --git a/lustre/include/linux/obd_support.h b/lustre/include/linux/obd_support.h index 2c54309..757dba2 100644 --- a/lustre/include/linux/obd_support.h +++ b/lustre/include/linux/obd_support.h @@ -37,7 +37,8 @@ extern atomic_t obd_memory; extern int obd_memmax; extern unsigned int obd_fail_loc; extern unsigned int obd_dump_on_timeout; -extern unsigned int obd_timeout; +extern unsigned int obd_timeout; /* seconds */ +#define PING_INTERVAL max(obd_timeout / 4, 1U) extern unsigned int ldlm_timeout; extern char obd_lustre_upcall[128]; extern unsigned int obd_sync_filter; @@ -90,6 +91,7 @@ extern wait_queue_head_t obd_race_waitq; #define OBD_FAIL_MDS_OST_SETATTR 0x12c #define OBD_FAIL_MDS_QUOTACHECK_NET 0x12d #define OBD_FAIL_MDS_QUOTACTL_NET 0x12e +#define OBD_FAIL_MDS_CLIENT_ADD 0x12f #define OBD_FAIL_OST 0x200 #define OBD_FAIL_OST_CONNECT_NET 0x201 @@ -132,6 +134,7 @@ extern wait_queue_head_t obd_race_waitq; #define OBD_FAIL_LDLM_ENQUEUE_BLOCKED 0x30b #define OBD_FAIL_LDLM_REPLY 0x30c #define OBD_FAIL_LDLM_RECOV_CLIENTS 0x30d +#define OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT 0x30e #define OBD_FAIL_OSC 0x400 #define OBD_FAIL_OSC_BRW_READ_BULK 0x401 @@ -140,12 +143,14 @@ extern wait_queue_head_t obd_race_waitq; #define OBD_FAIL_OSC_LOCK_CP_AST 0x404 #define OBD_FAIL_OSC_MATCH 0x405 #define OBD_FAIL_OSC_BRW_PREP_REQ 0x406 +#define OBD_FAIL_OSC_SHUTDOWN 0x407 #define OBD_FAIL_PTLRPC 0x500 #define OBD_FAIL_PTLRPC_ACK 0x501 #define OBD_FAIL_PTLRPC_RQBD 0x502 #define OBD_FAIL_PTLRPC_BULK_GET_NET 0x503 #define OBD_FAIL_PTLRPC_BULK_PUT_NET 0x504 +#define OBD_FAIL_PTLRPC_DROP_RPC 0x505 #define OBD_FAIL_OBD_PING_NET 0x600 #define OBD_FAIL_OBD_LOG_CANCEL_NET 0x601 @@ -191,7 +196,7 @@ do { \ #define OBD_FAIL_TIMEOUT(id, secs) \ do { \ - if (OBD_FAIL_CHECK_ONCE(id)) { \ + if (OBD_FAIL_CHECK_ONCE(id)) { \ CERROR("obd_fail_timeout id %x sleeping for %d secs\n", \ (id), (secs)); \ set_current_state(TASK_UNINTERRUPTIBLE); \ @@ -201,6 +206,38 @@ do { \ } \ } while(0) +/* Prefer the kernel's version, if it exports it, because it might be + * optimized for this CPU. */ +#if defined(__KERNEL__) && (defined(CONFIG_CRC32) || defined(CONFIG_CRC32_MODULE)) +# include +#else +/* crc32_le lifted from the Linux kernel, which had the following to say: + * + * This code is in the public domain; copyright abandoned. + * Liability for non-performance of this code is limited to the amount + * you paid for it. Since it is distributed for free, your refund will + * be very very small. If it breaks, you get to keep both pieces. + */ +#define CRCPOLY_LE 0xedb88320 +/** + * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 + * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for + * other uses, or the previous crc32 value if computing incrementally. + * @p - pointer to buffer over which CRC is run + * @len - length of buffer @p + */ +static inline __u32 crc32_le(__u32 crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} +#endif + #ifdef __KERNEL__ /* The idea here is to synchronise two threads to force a race. The * first thread that calls this with a matching fail_loc is put to @@ -219,7 +256,7 @@ do { \ } while(0) #else /* sigh. an expedient fix until OBD_RACE is fixed up */ -#define OBD_RACE(foo) LBUG() +#define OBD_RACE(foo) do {} while(0) #endif #define fixme() CDEBUG(D_OTHER, "FIXME\n"); @@ -227,37 +264,7 @@ do { \ #ifdef __KERNEL__ # include # include - - -# 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 static inline void OBD_FAIL_WRITE(int id, struct super_block *sb) { @@ -265,7 +272,7 @@ static inline void OBD_FAIL_WRITE(int id, struct super_block *sb) BDEVNAME_DECLARE_STORAGE(tmp); CERROR("obd_fail_loc=%x, fail write operation on %s\n", id, ll_bdevname(sb, tmp)); - ll_set_rdonly(ll_sbdev(sb)); + lvfs_set_rdonly(lvfs_sbdev(sb)); /* We set FAIL_ONCE because we never "un-fail" a device */ obd_fail_loc |= OBD_FAILED | OBD_FAIL_ONCE; } @@ -280,6 +287,20 @@ static inline void OBD_FAIL_WRITE(int id, struct super_block *sb) extern atomic_t portal_kmemory; +#if defined(LUSTRE_UTILS) /* this version is for utils only */ +#define OBD_ALLOC_GFP(ptr, size, gfp_mask) \ +do { \ + (ptr) = kmalloc(size, (gfp_mask)); \ + if ((ptr) == NULL) { \ + CERROR("kmalloc of '" #ptr "' (%d bytes) failed at %s:%d\n", \ + (int)(size), __FILE__, __LINE__); \ + } else { \ + memset(ptr, 0, size); \ + CDEBUG(D_MALLOC, "kmalloced '" #ptr "': %d at %p\n", \ + (int)(size), ptr); \ + } \ +} while (0) +#else /* this version is for the kernel and liblustre */ #define OBD_ALLOC_GFP(ptr, size, gfp_mask) \ do { \ (ptr) = kmalloc(size, (gfp_mask)); \ @@ -297,17 +318,13 @@ do { \ (int)(size), ptr, atomic_read(&obd_memory)); \ } \ } while (0) +#endif #ifndef OBD_GFP_MASK # define OBD_GFP_MASK GFP_NOFS #endif -#ifdef __KERNEL__ #define OBD_ALLOC(ptr, size) OBD_ALLOC_GFP(ptr, size, OBD_GFP_MASK) -#else -#define OBD_ALLOC(ptr, size) (ptr = malloc(size)) -#endif - #define OBD_ALLOC_WAIT(ptr, size) OBD_ALLOC_GFP(ptr, size, GFP_KERNEL) #ifdef __arch_um__ @@ -408,4 +425,7 @@ do { \ (ptr) = (void *)0xdeadbeef; \ } while (0) +#define KEY_IS(str) \ + (keylen == strlen(str) && memcmp(key, str, keylen) == 0) + #endif diff --git a/lustre/include/lustre/lustre_user.h b/lustre/include/lustre/lustre_user.h index 64c951d..9c4b9e5 100644 --- a/lustre/include/lustre/lustre_user.h +++ b/lustre/include/lustre/lustre_user.h @@ -9,6 +9,14 @@ #ifndef _LUSTRE_USER_H #define _LUSTRE_USER_H #include + +/* + * 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 #ifdef __KERNEL__ #include @@ -50,6 +58,7 @@ #define LL_FILE_IGNORE_LOCK 0x00000001 #define LL_FILE_GROUP_LOCKED 0x00000002 +#define LL_FILE_READAHEAD 0x00000004 #define LOV_USER_MAGIC_V1 0x0BD10BD0 #define LOV_USER_MAGIC LOV_USER_MAGIC_V1 @@ -133,7 +142,15 @@ struct if_quotacheck { }; #ifndef __KERNEL__ +#define NEED_QUOTA_DEFS +#else +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) +# define NEED_QUOTA_DEFS +# endif +#endif +#ifdef NEED_QUOTA_DEFS #ifndef QUOTABLOCK_BITS #define QUOTABLOCK_BITS 10 #endif @@ -170,6 +187,19 @@ struct if_dqblk { }; #endif +#ifndef QIF_BLIMITS +#define QIF_BLIMITS 1 +#define QIF_SPACE 2 +#define QIF_ILIMITS 4 +#define QIF_INODES 8 +#define QIF_BTIME 16 +#define QIF_ITIME 32 +#define QIF_LIMITS (QIF_BLIMITS | QIF_ILIMITS) +#define QIF_USAGE (QIF_SPACE | QIF_INODES) +#define QIF_TIMES (QIF_BTIME | QIF_ITIME) +#define QIF_ALL (QIF_LIMITS | QIF_USAGE | QIF_TIMES) +#endif + #endif /* !__KERNEL__ */ struct if_quotactl { diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686-smp.config index 3781659..e8ba99d 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686-smp.config @@ -588,6 +588,7 @@ CONFIG_SCSI_LOGGING=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m CONFIG_SCSI_7000FASST=m CONFIG_SCSI_ACARD=m CONFIG_SCSI_AHA152X=m @@ -621,6 +622,7 @@ CONFIG_SCSI_AM53C974=m CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -628,6 +630,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m CONFIG_SCSI_BUSLOGIC=m @@ -868,6 +871,17 @@ CONFIG_YELLOWFIN=m CONFIG_R8169=m CONFIG_SK98LIN=m CONFIG_TIGON3=m + +# +# Quadrics QsNet device support +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_FDDI=y CONFIG_DEFXX=m CONFIG_SKFP=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686.config index fce798b..e8ba99d 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-i686.config @@ -49,7 +49,7 @@ CONFIG_RWSEM_XCHGADD_ALGORITHM=y CONFIG_X86_L1_CACHE_SHIFT=7 CONFIG_X86_HAS_TSC=y CONFIG_X86_GOOD_APIC=y -# CONFIG_X86_PGE is not set +CONFIG_X86_PGE=y CONFIG_X86_USE_PPRO_CHECKSUM=y CONFIG_X86_PPRO_FENCE=y CONFIG_X86_F00F_WORKS_OK=y @@ -58,14 +58,7 @@ CONFIG_X86_MCE=y # # CPU Frequency scaling # -CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_24_API is not set -CONFIG_X86_POWERNOW_K6=m -# CONFIG_X86_LONGHAUL is not set -CONFIG_X86_SPEEDSTEP=m -# CONFIG_X86_P4_CLOCKMOD is not set -# CONFIG_X86_LONGRUN is not set -# CONFIG_X86_GX_SUSPMOD is not set +# CONFIG_CPU_FREQ is not set CONFIG_TOSHIBA=m CONFIG_I8K=m CONFIG_MICROCODE=m @@ -74,10 +67,11 @@ CONFIG_X86_CPUID=m # CONFIG_E820_PROC is not set CONFIG_EDD=m # CONFIG_NOHIGHMEM is not set -#CONFIG_HIGHMEM4G is not set +# CONFIG_HIGHMEM4G is not set CONFIG_HIGHMEM64G=y CONFIG_HIGHMEM=y CONFIG_HIGHPTE=y +CONFIG_X86_PAE=y CONFIG_HIGHIO=y # CONFIG_X86_4G is not set CONFIG_3GB=y @@ -85,21 +79,29 @@ CONFIG_3GB=y # CONFIG_1GB is not set # CONFIG_MATH_EMULATION is not set CONFIG_MTRR=y -# CONFIG_SMP is not set -# CONFIG_X86_UP_APIC is not set -# CONFIG_X86_UP_IOAPIC is not set -# CONFIG_X86_TSC_DISABLE is not set -CONFIG_X86_TSC=y +CONFIG_SMP=y +# CONFIG_NR_SIBLINGS_0 is not set +CONFIG_NR_SIBLINGS_2=y +CONFIG_SHARE_RUNQUEUE=y +CONFIG_MAX_NR_SIBLINGS=2 +CONFIG_X86_CLUSTERED_APIC=y +CONFIG_X86_NUMA=y +# CONFIG_X86_NUMAQ is not set +CONFIG_X86_SUMMIT=y +CONFIG_X86_CLUSTERED_APIC=y # CONFIG_NOBIGSTACK is not set CONFIG_STACK_SIZE_16KB=y # CONFIG_STACK_SIZE_32KB is not set # CONFIG_STACK_SIZE_64KB is not set CONFIG_STACK_SIZE_SHIFT=2 +CONFIG_HAVE_DEC_LOCK=y # # General setup # CONFIG_NET=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_LOCAL_APIC=y CONFIG_PCI=y # CONFIG_PCI_GOBIOS is not set # CONFIG_PCI_GODIRECT is not set @@ -125,10 +127,10 @@ CONFIG_I82365=y # # PCI Hotplug Support # -# CONFIG_HOTPLUG_PCI is not set -# CONFIG_HOTPLUG_PCI_COMPAQ is not set +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_COMPAQ=m # CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set -# CONFIG_HOTPLUG_PCI_IBM is not set +CONFIG_HOTPLUG_PCI_IBM=m # CONFIG_HOTPLUG_PCI_ACPI is not set CONFIG_SYSVIPC=y CONFIG_BSD_PROCESS_ACCT=y @@ -586,6 +588,7 @@ CONFIG_SCSI_LOGGING=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m CONFIG_SCSI_7000FASST=m CONFIG_SCSI_ACARD=m CONFIG_SCSI_AHA152X=m @@ -619,6 +622,7 @@ CONFIG_SCSI_AM53C974=m CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -626,6 +630,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m CONFIG_SCSI_BUSLOGIC=m @@ -799,7 +804,7 @@ CONFIG_DEPCA=m CONFIG_HP100=m CONFIG_NET_ISA=y CONFIG_E2100=m -CONFIG_EWRK3=m +# CONFIG_EWRK3 is not set CONFIG_EEXPRESS=m CONFIG_EEXPRESS_PRO=m CONFIG_HPLAN_PLUS=m @@ -828,7 +833,7 @@ CONFIG_LNE390=m CONFIG_FEALNX=m CONFIG_NATSEMI=m CONFIG_NE2K_PCI=m -# CONFIG_FORCEDETH is not set +CONFIG_FORCEDETH=m CONFIG_NE3210=m CONFIG_ES3210=m CONFIG_8139CP=m @@ -866,6 +871,17 @@ CONFIG_YELLOWFIN=m CONFIG_R8169=m CONFIG_SK98LIN=m CONFIG_TIGON3=m + +# +# Quadrics QsNet device support +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_FDDI=y CONFIG_DEFXX=m CONFIG_SKFP=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64-smp.config index 4058f4b..94fca767 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64-smp.config @@ -536,6 +536,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m # CONFIG_SCSI_7000FASST is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set @@ -569,6 +570,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -576,6 +578,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64.config index 7e440cf..94fca767 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-ia64.config @@ -53,7 +53,7 @@ CONFIG_HUGETLB_PAGE_SIZE_256MB=y # CONFIG_HUGETLB_PAGE_SIZE_1MB is not set # CONFIG_HUGETLB_PAGE_SIZE_256KB is not set # CONFIG_IA64_PAL_IDLE is not set -# CONFIG_SMP is not set +CONFIG_SMP=y CONFIG_IA32_SUPPORT=y CONFIG_COMPAT=y CONFIG_PERFMON=y @@ -536,6 +536,7 @@ CONFIG_SCSI_CONSTANTS=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m # CONFIG_SCSI_7000FASST is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set @@ -569,6 +570,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -576,6 +578,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64-smp.config index 5be9ee4..594b821 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64-smp.config @@ -543,6 +543,7 @@ CONFIG_SCSI_LOGGING=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m # CONFIG_SCSI_7000FASST is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set @@ -575,6 +576,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -582,6 +584,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set @@ -777,6 +780,17 @@ CONFIG_YELLOWFIN=m CONFIG_R8169=m CONFIG_SK98LIN=m CONFIG_TIGON3=m + +# +# Quadrics QsNet device support +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_FDDI=y CONFIG_DEFXX=m CONFIG_SKFP=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64.config b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64.config index a32b0ba..594b821 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.4.21-rhel-2.4-x86_64.config @@ -42,13 +42,17 @@ CONFIG_X86_CPUID=m CONFIG_X86_IO_APIC=y CONFIG_X86_LOCAL_APIC=y CONFIG_MTRR=y -# CONFIG_SMP is not set +CONFIG_SMP=y CONFIG_HPET_TIMER=y CONFIG_GART_IOMMU=y # CONFIG_SWIOTLB is not set -CONFIG_X86_UP_IOAPIC=y +CONFIG_NR_SIBLINGS_0=y +# CONFIG_NR_SIBLINGS_2 is not set +CONFIG_HAVE_DEC_LOCK=y CONFIG_MCE=y -# CONFIG_K8_NUMA is not set +CONFIG_K8_NUMA=y +CONFIG_DISCONTIGMEM=y +CONFIG_NUMA=y # CONFIG_NOBIGSTACK is not set CONFIG_STACK_SIZE_16KB=y # CONFIG_STACK_SIZE_32KB is not set @@ -108,6 +112,7 @@ CONFIG_ACPI_BUTTON=m CONFIG_ACPI_FAN=m CONFIG_ACPI_PROCESSOR=m CONFIG_ACPI_THERMAL=m +# CONFIG_ACPI_NUMA is not set # CONFIG_ACPI_ASUS is not set # CONFIG_ACPI_TOSHIBA is not set # CONFIG_ACPI_DEBUG is not set @@ -538,6 +543,7 @@ CONFIG_SCSI_LOGGING=y # SCSI low-level drivers # CONFIG_BLK_DEV_3W_XXXX_RAID=m +CONFIG_BLK_DEV_3W_9XXX_RAID=m # CONFIG_SCSI_7000FASST is not set # CONFIG_SCSI_ACARD is not set # CONFIG_SCSI_AHA152X is not set @@ -570,6 +576,7 @@ CONFIG_AIC7XXX_OLD_PROC_STATS=y CONFIG_SCSI_MEGARAID=m CONFIG_SCSI_MEGARAID2=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m CONFIG_SCSI_SATA_NV=m @@ -577,6 +584,7 @@ CONFIG_SCSI_SATA_PROMISE=m CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m +CONFIG_SCSI_SATA_ULI=m CONFIG_SCSI_SATA_VIA=m CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set @@ -772,6 +780,17 @@ CONFIG_YELLOWFIN=m CONFIG_R8169=m CONFIG_SK98LIN=m CONFIG_TIGON3=m + +# +# Quadrics QsNet device support +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_FDDI=y CONFIG_DEFXX=m CONFIG_SKFP=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686-bigsmp.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686-bigsmp.config index 06e4e8b..beb776a 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686-bigsmp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686-bigsmp.config @@ -201,6 +201,8 @@ CONFIG_APM_DISPLAY_BLANK=y # CONFIG_APM_RTC_IS_GMT is not set CONFIG_APM_ALLOW_INTS=y # CONFIG_APM_REAL_MODE_POWER_OFF is not set +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # CPU Frequency scaling @@ -1310,6 +1312,17 @@ CONFIG_SKISA=m CONFIG_PROTEON=m CONFIG_ABYSS=m CONFIG_SMCTR=m + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_RCPCI=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686.config index e90083d..beb776a 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-i686.config @@ -201,6 +201,8 @@ CONFIG_APM_DISPLAY_BLANK=y # CONFIG_APM_RTC_IS_GMT is not set CONFIG_APM_ALLOW_INTS=y # CONFIG_APM_REAL_MODE_POWER_OFF is not set +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # CPU Frequency scaling @@ -589,6 +591,7 @@ CONFIG_SCSI_PROC_FS=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=m +CONFIG_SD_IOSTATS=y CONFIG_CHR_DEV_ST=m CONFIG_CHR_DEV_OSST=m CONFIG_BLK_DEV_SR=m @@ -1309,6 +1312,17 @@ CONFIG_SKISA=m CONFIG_PROTEON=m CONFIG_ABYSS=m CONFIG_SMCTR=m + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_RCPCI=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64-smp.config index 6844769..48f481b 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64-smp.config @@ -123,6 +123,8 @@ CONFIG_ACPI_POWER=y CONFIG_ACPI_PCI=y CONFIG_ACPI_SYSTEM=y CONFIG_ACPI_INITRD=y +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # Bus options (PCI, PCMCIA) @@ -1027,6 +1029,17 @@ CONFIG_IBMOL=m CONFIG_IBMLS=m CONFIG_3C359=m # CONFIG_TMS380TR is not set + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_SHAPER=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64.config index 6844769..48f481b 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-ia64.config @@ -123,6 +123,8 @@ CONFIG_ACPI_POWER=y CONFIG_ACPI_PCI=y CONFIG_ACPI_SYSTEM=y CONFIG_ACPI_INITRD=y +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # Bus options (PCI, PCMCIA) @@ -1027,6 +1029,17 @@ CONFIG_IBMOL=m CONFIG_IBMLS=m CONFIG_3C359=m # CONFIG_TMS380TR is not set + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_SHAPER=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64-smp.config index 5afe34e..0730819 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64-smp.config @@ -137,6 +137,8 @@ CONFIG_ACPI_POWER=y CONFIG_ACPI_PCI=y CONFIG_ACPI_SYSTEM=y CONFIG_ACPI_INITRD=y +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # CPU Frequency scaling @@ -1109,6 +1111,17 @@ CONFIG_3C359=m CONFIG_TMS380TR=m CONFIG_TMSPCI=m CONFIG_ABYSS=m + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_SHAPER=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64.config index 5afe34e..0730819 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.5-2.6-suse-x86_64.config @@ -137,6 +137,8 @@ CONFIG_ACPI_POWER=y CONFIG_ACPI_PCI=y CONFIG_ACPI_SYSTEM=y CONFIG_ACPI_INITRD=y +CONFIG_IOPROC=y +CONFIG_PTRACK=y # # CPU Frequency scaling @@ -1109,6 +1111,17 @@ CONFIG_3C359=m CONFIG_TMS380TR=m CONFIG_TMSPCI=m CONFIG_ABYSS=m + +# +# Quadrics QsNet +# +CONFIG_QSNET=m +CONFIG_ELAN3=m +CONFIG_ELAN4=m +CONFIG_EP=m +CONFIG_EIP=m +CONFIG_RMS=m +CONFIG_JTAG=m CONFIG_NET_FC=y CONFIG_NET_LPFC=m CONFIG_SHAPER=m diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686-smp.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686-smp.config index f8e83ec..18cb4d1 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686-smp.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686-smp.config @@ -1,28 +1,34 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-prep +# Fri May 13 14:09:31 2005 # +CONFIG_X86=y CONFIG_MMU=y -CONFIG_SMP=y -# CONFIG_HOTPLUG_CPU is not set -CONFIG_LOCALVERSION="" +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y # CONFIG_BSD_PROCESS_ACCT_V3 is not set CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y @@ -33,9 +39,10 @@ CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y -# CONFIG_PREEMPT is not set -CONFIG_PREEMPT_VOLUNTARY=y -CONFIG_POSIX_MQUEUE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,14 +52,188 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y CONFIG_MODVERSIONS=y -CONFIG_KMOD=y CONFIG_MODULE_SIG=y # CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +# CONFIG_X86_PC is not set +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +CONFIG_X86_GENERICARCH=y +# CONFIG_X86_ES7000 is not set +CONFIG_X86_CYCLONE_TIMER=y +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +CONFIG_M686=y +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_X86_4G is not set +# CONFIG_X86_SWITCH_PAGETABLES is not set +# CONFIG_X86_4G_VM_LAYOUT is not set +# CONFIG_X86_UACCESS_INDIRECT is not set +# CONFIG_X86_HIGH_ENTRY is not set +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_SMP=y +CONFIG_NR_CPUS=32 +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_X86_MCE_P4THERMAL=y +CONFIG_TOSHIBA=m +CONFIG_I8K=m +CONFIG_MICROCODE=m +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +# CONFIG_HIGHMEM4G is not set +CONFIG_HIGHMEM64G=y +CONFIG_HIGHMEM=y +CONFIG_X86_PAE=y +# CONFIG_NUMA is not set +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +# CONFIG_IRQBALANCE is not set +CONFIG_HAVE_DEC_LOCK=y +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=y +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y # # Bus options (PCI, PCMCIA, EISA, MCA, ISA) # CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y CONFIG_PCI_MSI=y CONFIG_PCI_LEGACY_PROC=y # CONFIG_PCI_NAMES is not set @@ -60,22 +241,37 @@ CONFIG_ISA=y # CONFIG_EISA is not set # CONFIG_MCA is not set # CONFIG_SCx200 is not set -CONFIG_HOTPLUG=y # # PCMCIA/CardBus support # CONFIG_PCMCIA=m # CONFIG_PCMCIA_DEBUG is not set -CONFIG_YENTA=y +CONFIG_YENTA=m CONFIG_CARDBUS=y +CONFIG_PD6729=m # CONFIG_I82092 is not set CONFIG_I82365=m -CONFIG_PD6729=m CONFIG_TCIC=m CONFIG_PCMCIA_PROBE=y # +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_IBM=m +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# # Executable file formats # CONFIG_BINFMT_ELF=y @@ -89,18 +285,23 @@ CONFIG_BINFMT_MISC=y # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set # # Memory Technology Devices (MTD) # CONFIG_MTD=m # CONFIG_MTD_DEBUG is not set -CONFIG_MTD_PARTITIONS=m +CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_PARTITIONS=y + # # User Modules And Translation Layers # @@ -119,13 +320,24 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m -# CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access @@ -135,23 +347,19 @@ CONFIG_MTD_ABSENT=m # CONFIG_MTD_PNC2000 is not set CONFIG_MTD_SC520CDP=m CONFIG_MTD_NETSC520=m -CONFIG_MTD_SBC_GXX=m -CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m # CONFIG_MTD_AMD76XROM is not set # CONFIG_MTD_SCB2_FLASH is not set # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set # CONFIG_MTD_L440GX is not set -# CONFIG_MTD_PCI is not set # # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_PMC551_BUGFIX is not set -# CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -163,9 +371,6 @@ CONFIG_MTDRAM_ERASE_SIZE=128 # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set -CONFIG_MTD_DOCPROBE=m -# CONFIG_MTD_DOCPROBE_ADVANCED is not set -CONFIG_MTD_DOCPROBE_ADDRESS=0 # # NAND Flash Device Drivers @@ -173,15 +378,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m - -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set -CONFIG_MTD_CFI_AMDSTD_RETRY=3 -# CONFIG_MTD_ICHXROM is not set -# CONFIG_MTD_PHRAM is not set # CONFIG_MTD_NAND_DISKONCHIP is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set - # # Parallel port support @@ -222,45 +419,41 @@ CONFIG_BLK_DEV_DAC960=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=16384 CONFIG_BLK_DEV_INITRD=y -CONFIG_BLK_DEV_ATIIXP=y -CONFIG_BLK_DEV_DELKIN=y -CONFIG_BLK_DEV_IT8212=y CONFIG_LBD=y -# CONFIG_DCSSBLK is not set - +CONFIG_DISKDUMP=m # # ATA/ATAPI/MFM/RLL support # CONFIG_IDE=y -CONFIG_IDE_GENERIC=y -# CONFIG_HPT34X_AUTODMA is not set - CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_IDEDISK_MULTI_MODE=y CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set CONFIG_BLK_DEV_IDEFLOPPY=y CONFIG_BLK_DEV_IDESCSI=m # CONFIG_IDE_TASK_IOCTL is not set # CONFIG_IDE_TASKFILE_IO is not set -# CONFIG_BLK_DEV_IDE_SATA is not set # # IDE chipset support/bugfixes # +CONFIG_IDE_GENERIC=y # CONFIG_BLK_DEV_CMD640 is not set -# CONFIG_BLK_DEV_CMD640_ENHANCED is not set CONFIG_BLK_DEV_IDEPNP=y CONFIG_BLK_DEV_IDEPCI=y CONFIG_IDEPCI_SHARE_IRQ=y @@ -276,15 +469,18 @@ CONFIG_BLK_DEV_AEC62XX=y CONFIG_BLK_DEV_ALI15X3=y # CONFIG_WDC_ALI15X3 is not set CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y CONFIG_BLK_DEV_CMD64X=y CONFIG_BLK_DEV_TRIFLEX=y CONFIG_BLK_DEV_CY82C693=y CONFIG_BLK_DEV_CS5520=y CONFIG_BLK_DEV_CS5530=y CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set CONFIG_BLK_DEV_HPT366=y # CONFIG_BLK_DEV_SC1200 is not set CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y # CONFIG_BLK_DEV_NS87415 is not set CONFIG_BLK_DEV_PDC202XX_OLD=y # CONFIG_PDC202XX_BURST is not set @@ -296,6 +492,7 @@ CONFIG_BLK_DEV_SIS5513=y CONFIG_BLK_DEV_SLC90E66=y # CONFIG_BLK_DEV_TRM290 is not set CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set # CONFIG_IDE_CHIPSETS is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set @@ -312,6 +509,8 @@ CONFIG_SCSI_PROC_FS=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=m +CONFIG_SCSI_DUMP=m +CONFIG_SD_IOSTATS=y CONFIG_CHR_DEV_ST=m CONFIG_CHR_DEV_OSST=m CONFIG_BLK_DEV_SR=m @@ -324,8 +523,12 @@ CONFIG_CHR_DEV_SG=m # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y -CONFIG_SCSI_SPI_ATTRS=y -CONFIG_SCSI_FC_ATTRS=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m # # SCSI low-level drivers @@ -340,8 +543,6 @@ CONFIG_SCSI_AACRAID=m CONFIG_SCSI_AIC7XXX=m CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 CONFIG_AIC7XXX_RESET_DELAY_MS=15000 -# CONFIG_AIC7XXX_PROBE_EISA_VL is not set -# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set # CONFIG_AIC7XXX_DEBUG_ENABLE is not set CONFIG_AIC7XXX_DEBUG_MASK=0 # CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set @@ -349,42 +550,38 @@ CONFIG_SCSI_AIC7XXX_OLD=m CONFIG_SCSI_AIC79XX=m CONFIG_AIC79XX_CMDS_PER_DEVICE=4 CONFIG_AIC79XX_RESET_DELAY_MS=15000 -# CONFIG_AIC79XX_BUILD_FIRMWARE is not set # CONFIG_AIC79XX_ENABLE_RD_STRM is not set # CONFIG_AIC79XX_DEBUG_ENABLE is not set CONFIG_AIC79XX_DEBUG_MASK=0 # CONFIG_AIC79XX_REG_PRETTY_PRINT is not set -# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_DPT_I2O is not set # CONFIG_SCSI_IN2000 is not set CONFIG_MEGARAID_NEWGEN=y CONFIG_MEGARAID_MM=m CONFIG_MEGARAID_MAILBOX=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m CONFIG_SCSI_SATA_PROMISE=m -CONFIG_SCSI_SATA_VIA=m -CONFIG_BLK_DEV_SX8=m -CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m -CONFIG_SCSI_SATA_SX4=m -CONFIG_SCSI_SATA_NV=m -CONFIG_SCSI_SATA_AHCI=m - +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set -CONFIG_SCSI_INITIO=m -# CONFIG_SCSI_OMIT_FLASHPOINT is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_DTC3280 is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m CONFIG_SCSI_FUTURE_DOMAIN=m CONFIG_SCSI_GDTH=m # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m # CONFIG_SCSI_INIA100 is not set CONFIG_SCSI_PPA=m CONFIG_SCSI_IMM=m @@ -396,21 +593,13 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set -# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set CONFIG_SCSI_QLOGIC_1280=m -# CONFIG_SCSI_SYM53C416 is not set -# CONFIG_SCSI_DC395x is not set -# CONFIG_SCSI_T128 is not set -# CONFIG_SCSI_U14_34F is not set -# CONFIG_SCSI_ULTRASTOR is not set -# CONFIG_SCSI_NSP32 is not set -# CONFIG_SCSI_DEBUG is not set -# CONFIG_SCSI_DC390T is not set CONFIG_SCSI_QLA2XXX=m CONFIG_SCSI_QLA21XX=m CONFIG_SCSI_QLA22XX=m @@ -418,11 +607,15 @@ CONFIG_SCSI_QLA2300=m CONFIG_SCSI_QLA2322=m CONFIG_SCSI_QLA6312=m CONFIG_SCSI_QLA6322=m -# CONFIG_SCSI_IPR is not set -# CONFIG_SCSI_DPT_I2O is not set - -CONFIG_SCSI_LPFC=m - +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + # # PCMCIA SCSI adapter support # @@ -432,7 +625,6 @@ CONFIG_PCMCIA_FDOMAIN=m # CONFIG_PCMCIA_QLOGIC is not set # CONFIG_PCMCIA_SYM53C500 is not set - # # Old CD-ROM drivers (not SCSI, not IDE) # @@ -446,15 +638,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=m CONFIG_MD_RAID6=m -CONFIG_MD_RAID10=m CONFIG_MD_MULTIPATH=m CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m -CONFIG_DM_CRYPT=m # # Fusion MPT device support @@ -465,39 +657,18 @@ CONFIG_FUSION_CTL=m CONFIG_FUSION_LAN=m # -# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set # -# Subsystem Options -# -# CONFIG_IEEE1394_VERBOSEDEBUG is not set -CONFIG_IEEE1394_OUI_DB=y - -# -# Device Drivers -# -# CONFIG_IEEE1394_PCILYNX is not set -CONFIG_IEEE1394_OHCI1394=m - -# -# Protocol Drivers -# -CONFIG_IEEE1394_VIDEO1394=m -CONFIG_IEEE1394_SBP2=m -# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set -# CONFIG_IEEE1394_ETH1394 is not set -CONFIG_IEEE1394_DV1394=m -CONFIG_IEEE1394_RAWIO=m -CONFIG_IEEE1394_CMP=m -CONFIG_IEEE1394_AMDTP=m -# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set - -# # I2O device support # CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m # # Networking support @@ -513,7 +684,6 @@ CONFIG_NETLINK_DEV=y CONFIG_UNIX=y CONFIG_NET_KEY=m CONFIG_INET=y -CONFIG_INET_TUNNEL=m CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y @@ -532,14 +702,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m -CONFIG_NETCONSOLE=m -# CONFIG_NETPOLL_RX is not set -CONFIG_NETPOLL_TRAP=y -CONFIG_NET_POLL_CONTROLLER=y -CONFIG_NETDUMP=m -CONFIG_DISKDUMP=m -CONFIG_SCSI_DUMP=m -CONFIG_SD_IOSTATS=y +CONFIG_INET_TUNNEL=m # # IP: Virtual Server Configuration @@ -547,10 +710,18 @@ CONFIG_SD_IOSTATS=y CONFIG_IP_VS=m # CONFIG_IP_VS_DEBUG is not set CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# CONFIG_IP_VS_PROTO_TCP=y CONFIG_IP_VS_PROTO_UDP=y CONFIG_IP_VS_PROTO_ESP=y CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# CONFIG_IP_VS_RR=m CONFIG_IP_VS_WRR=m CONFIG_IP_VS_LC=m @@ -561,17 +732,18 @@ CONFIG_IP_VS_DH=m CONFIG_IP_VS_SH=m CONFIG_IP_VS_SED=m CONFIG_IP_VS_NQ=m -CONFIG_IP_VS_FTP=m - +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m CONFIG_IPV6=m CONFIG_IPV6_PRIVACY=y CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m CONFIG_IPV6_TUNNEL=m -# CONFIG_DECNET is not set -CONFIG_BRIDGE=m CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set CONFIG_BRIDGE_NETFILTER=y @@ -580,14 +752,14 @@ CONFIG_BRIDGE_NETFILTER=y # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_RAW=m CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_COMMENT=m CONFIG_IP_NF_MATCH_LIMIT=m CONFIG_IP_NF_MATCH_IPRANGE=m CONFIG_IP_NF_MATCH_MAC=m @@ -610,15 +782,18 @@ CONFIG_IP_NF_MATCH_PHYSDEV=m CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_REALM=m CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_REDIRECT=m CONFIG_IP_NF_TARGET_NETMAP=m CONFIG_IP_NF_TARGET_SAME=m -CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_NAT_LOCAL=y CONFIG_IP_NF_NAT_SNMP_BASIC=m CONFIG_IP_NF_NAT_IRC=m @@ -631,14 +806,11 @@ CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_DSCP=m CONFIG_IP_NF_TARGET_MARK=m CONFIG_IP_NF_TARGET_CLASSIFY=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m -CONFIG_IP_NF_CT_ACCT=y -CONFIG_IP_NF_CT_PROTO_SCTP=m # CONFIG_IP_NF_COMPAT_IPCHAINS is not set # CONFIG_IP_NF_COMPAT_IPFWADM is not set @@ -667,7 +839,6 @@ CONFIG_IP6_NF_MANGLE=m CONFIG_IP6_NF_TARGET_MARK=m CONFIG_IP6_NF_RAW=m - # # Bridge: Netfilter Configuration # @@ -703,33 +874,36 @@ CONFIG_IP_SCTP=m # CONFIG_SCTP_HMAC_SHA1 is not set CONFIG_SCTP_HMAC_MD5=y CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_LLC=m +# CONFIG_DECNET is not set +CONFIG_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set -# CONFIG_IPX_INTERN is not set # CONFIG_ATALK is not set -# CONFIG_DEV_APPLETALK is not set -# CONFIG_LTPC is not set -# CONFIG_COPS is not set -CONFIG_COPS_DAYNA=y -CONFIG_COPS_TANGENT=y -# CONFIG_IPDDP is not set -CONFIG_IPDDP_ENCAP=y -CONFIG_IPDDP_DECAP=y # CONFIG_X25 is not set # CONFIG_LAPB is not set CONFIG_NET_DIVERT=y # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing # CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m @@ -737,9 +911,8 @@ CONFIG_NET_SCH_TEQL=m CONFIG_NET_SCH_TBF=m CONFIG_NET_SCH_GRED=m CONFIG_NET_SCH_DSMARK=m -CONFIG_NET_SCH_INGRESS=m -CONFIG_NET_SCH_HFSC=m CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m CONFIG_NET_QOS=y CONFIG_NET_ESTIMATOR=y CONFIG_NET_CLS=y @@ -748,24 +921,59 @@ CONFIG_NET_CLS_ROUTE4=m CONFIG_NET_CLS_ROUTE=y CONFIG_NET_CLS_FW=m CONFIG_NET_CLS_U32=m -CONFIG_NET_CLS_RSVP=m -CONFIG_NET_CLS_RSVP6=m -CONFIG_NET_CLS_POLICE=y -# CONFIG_NET_ACT_POLICE is not set CONFIG_CLS_U32_PERF=y CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m # CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y # # Network testing # # CONFIG_NET_PKTGEN is not set -CONFIG_NETDEVICES=y +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m # -# ARCnet devices +# Bluetooth device drivers # -# CONFIG_ARCNET is not set +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_BONDING=m # CONFIG_EQUALIZER is not set @@ -774,46 +982,9 @@ CONFIG_ETHERTAP=m # CONFIG_NET_SB1000 is not set # -# ATM -# -CONFIG_ATM_CLIP=m -CONFIG_ATM_LANE=m -CONFIG_ATM_BR2684=m -CONFIG_NET_SCH_ATM=m -CONFIG_ATM_TCP=m -CONFIG_ATM_LANAI=m -CONFIG_ATM_ENI=m -CONFIG_ATM_FIRESTREAM=m -# CONFIG_ATM_ZATM is not set -CONFIG_ATM_IDT77252=m -CONFIG_ATM_AMBASSADOR=m -CONFIG_ATM_HORIZON=m -CONFIG_ATM_FORE200E_MAYBE=m -CONFIG_ATM_HE=m -CONFIG_PPPOATM=m -CONFIG_ATM_NICSTAR=m -# CONFIG_ATM_IA is not set - - -# CONFIG_ATM_CLIP_NO_ICMP is not set -# CONFIG_ATM_MPOA is not set -# CONFIG_ATM_BR2684_IPFILTER is not set -# CONFIG_ATM_ENI_DEBUG is not set -# CONFIG_ATM_ENI_TUNE_BURST is not set -# CONFIG_ATM_ZATM_DEBUG is not set -# CONFIG_ATM_IDT77252_DEBUG is not set -# CONFIG_ATM_IDT77252_RCV_ALL is not set -# CONFIG_ATM_AMBASSADOR_DEBUG is not set -# CONFIG_ATM_HORIZON_DEBUG is not set -# CONFIG_ATM_FORE200E_PCA is not set -# CONFIG_ATM_HE_USE_SUNI is not set -# CONFIG_ATM_NICSTAR_USE_SUNI is not set -# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set -# CONFIG_ATM_IA_DEBUG is not set - - - - +# ARCnet devices +# +# CONFIG_ARCNET is not set # # Ethernet (10 or 100Mbit) @@ -826,7 +997,6 @@ CONFIG_NET_VENDOR_3COM=y # CONFIG_EL1 is not set # CONFIG_EL2 is not set # CONFIG_ELPLUS is not set -# CONFIG_ELPLUS is not set # CONFIG_EL16 is not set # CONFIG_EL3 is not set # CONFIG_3C515 is not set @@ -847,12 +1017,9 @@ CONFIG_NET_VENDOR_RACAL=y CONFIG_NET_TULIP=y CONFIG_DE2104X=m CONFIG_TULIP=m -# CONFIG_TULIP_NAPI is not set - # CONFIG_TULIP_MWI is not set CONFIG_TULIP_MMIO=y -# CONFIG_NI5010 is not set -# CONFIG_PCMCIA_XIRTULIP is not set +# CONFIG_TULIP_NAPI is not set CONFIG_DE4X5=m CONFIG_WINBOND_840=m CONFIG_DM9102=m @@ -861,20 +1028,6 @@ CONFIG_PCMCIA_XIRCOM=m # CONFIG_DEPCA is not set CONFIG_HP100=m # CONFIG_NET_ISA is not set -# CONFIG_EWRK3 is not set -CONFIG_E2100=m -CONFIG_EEXPRESS=m -CONFIG_EEXPRESS_PRO=m -CONFIG_HPLAN_PLUS=m -CONFIG_HPLAN=m -CONFIG_LP486E=m -CONFIG_ETH16I=m -CONFIG_NE2000=m -CONFIG_ZNET=m -CONFIG_SEEQ8005=m -CONFIG_LNE390=m -CONFIG_NE3210=m -CONFIG_ES3210=m CONFIG_NET_PCI=y CONFIG_PCNET32=m CONFIG_AMD8111_ETH=m @@ -884,6 +1037,7 @@ CONFIG_ADAPTEC_STARFIRE_NAPI=y # CONFIG_AC3200 is not set CONFIG_APRICOT=m CONFIG_B44=m +CONFIG_FORCEDETH=m # CONFIG_CS89x0 is not set # CONFIG_DGRS is not set CONFIG_EEPRO100=m @@ -891,7 +1045,6 @@ CONFIG_EEPRO100=m CONFIG_E100=m CONFIG_E100_NAPI=y CONFIG_FEALNX=m -CONFIG_FORCEDETH=m CONFIG_NATSEMI=m CONFIG_NE2K_PCI=m CONFIG_8139CP=m @@ -903,11 +1056,9 @@ CONFIG_8139TOO_8129=y CONFIG_SIS900=m CONFIG_EPIC100=m # CONFIG_SUNDANCE is not set -# CONFIG_SUNDANCE_MMIO is not set CONFIG_TLAN=m CONFIG_VIA_RHINE=m CONFIG_VIA_RHINE_MMIO=y -CONFIG_VIA_VELOCITY=m CONFIG_NET_POCKET=y # CONFIG_ATP is not set # CONFIG_DE600 is not set @@ -927,6 +1078,7 @@ CONFIG_NS83820=m CONFIG_R8169=m CONFIG_R8169_NAPI=y CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m CONFIG_TIGON3=m # @@ -936,21 +1088,21 @@ CONFIG_IXGB=m CONFIG_IXGB_NAPI=y CONFIG_S2IO=m CONFIG_S2IO_NAPI=y -CONFIG_FDDI=y -# CONFIG_DEFXX is not set -# CONFIG_SKFP is not set -# CONFIG_HIPPI is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m -CONFIG_PPP_MULTILINK=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_IPPP_FILTER=y -# CONFIG_PPP_BSDCOMP is not set -CONFIG_PPPOE=m -# CONFIG_SLIP is not set + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m # # Wireless LAN (non-hamradio) @@ -992,7 +1144,6 @@ CONFIG_TMD_HERMES=m CONFIG_PCI_HERMES=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m -CONFIG_PRISM54=m # # Wireless 802.11b Pcmcia/Cardbus cards support @@ -1001,32 +1152,12 @@ CONFIG_PCMCIA_HERMES=m CONFIG_AIRO_CS=m CONFIG_PCMCIA_ATMEL=m CONFIG_PCMCIA_WL3501=m -CONFIG_NET_WIRELESS=y - -# -# Token Ring devices -# -CONFIG_TR=y -CONFIG_IBMOL=m -CONFIG_3C359=m -CONFIG_TMS380TR=m -CONFIG_TMSPCI=m -CONFIG_ABYSS=m -CONFIG_IBMTR=m -CONFIG_IBMLS=m -CONFIG_SKISA=m -CONFIG_PROTEON=m -CONFIG_SMCTR=m -CONFIG_PCMCIA_IBMTR=m - - -CONFIG_NET_FC=y -# CONFIG_SHAPER is not set # -# Wan interfaces +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support # -# CONFIG_WAN is not set +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y # # PCMCIA network device support @@ -1040,119 +1171,104 @@ CONFIG_PCMCIA_NMCLAN=m CONFIG_PCMCIA_SMC91C92=m CONFIG_PCMCIA_XIRC2PS=m CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m # -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support -# -# CONFIG_IRDA is not set -# CONFIG_IRDA is not set -# CONFIG_IRDA_DEBUG is not set -CONFIG_IRLAN=m -CONFIG_IRNET=m -CONFIG_IRCOMM=m -# CONFIG_IRDA_ULTRA is not set -CONFIG_IRDA_CACHE_LAST_LSAP=y -CONFIG_IRDA_FAST_RR=y -CONFIG_IRTTY_SIR=m -CONFIG_DONGLE=y -CONFIG_ESI_DONGLE=m -CONFIG_ACTISYS_DONGLE=m -CONFIG_TEKRAM_DONGLE=m -CONFIG_IRPORT_SIR=m -# CONFIG_DONGLE_OLD is not set -CONFIG_LITELINK_DONGLE=m -CONFIG_MA600_DONGLE=m -CONFIG_GIRBIL_DONGLE=m -CONFIG_MCP2120_DONGLE=m -CONFIG_OLD_BELKIN_DONGLE=m -CONFIG_ACT200L_DONGLE=m - -CONFIG_USB_IRDA=m -CONFIG_NSC_FIR=m -CONFIG_SIGMATEL_FIR=m -# CONFIG_WINBOND_FIR is not set -# CONFIG_TOSHIBA_FIR is not set -# CONFIG_SMC_IRCC_FIR is not set -# CONFIG_ALI_FIR is not set -# CONFIG_VLSI_FIR is not set -# CONFIG_VIA_FIR is not set - - - -# -# Bluetooth support +# Wan interfaces # -CONFIG_BT=m -CONFIG_BT_L2CAP=m -CONFIG_BT_SCO=m -CONFIG_BT_CMTP=m -CONFIG_BT_RFCOMM=m -CONFIG_BT_RFCOMM_TTY=y -CONFIG_BT_BNEP=m -CONFIG_BT_BNEP_MC_FILTER=y -CONFIG_BT_BNEP_PROTO_FILTER=y -CONFIG_BT_HIDP=m +# CONFIG_WAN is not set # -# Bluetooth device drivers +# ATM drivers # -CONFIG_BT_HCIUSB=m -CONFIG_BT_HCIUART=m -CONFIG_BT_HCIUART_H4=y -CONFIG_BT_HCIUART_BCSP=y -CONFIG_BT_HCIUART_BCSP_TXCRC=y -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m -CONFIG_BT_HCIBTUART=m -CONFIG_BT_HCIVHCI=m -CONFIG_BT_HCIUSB_SCO=y -CONFIG_BT_HCIBCM203X=m -CONFIG_BT_HCIBFUSB=m -CONFIG_USB_BLUETOOTH_TTY=m +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +# CONFIG_SKFP is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +# CONFIG_SLIP is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m +CONFIG_NETDUMP=m # # ISDN subsystem # - CONFIG_ISDN=m -CONFIG_ISDN_I4L=m -CONFIG_ISDN_DRV_AVMB1_B1ISA=m -CONFIG_ISDN_DRV_AVMB1_B1PCI=m -CONFIG_ISDN_DRV_AVMB1_T1ISA=m -CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m -CONFIG_ISDN_DRV_AVMB1_T1PCI=m -CONFIG_ISDN_DRV_AVMB1_C4=m +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m CONFIG_ISDN_PPP=y CONFIG_ISDN_PPP_VJ=y CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y # CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y CONFIG_ISDN_TTY_FAX=y -CONFIG_DE_AOC=y -CONFIG_ISDN_AUDIO=y +# +# ISDN feature submodules +# -CONFIG_ISDN_DRV_HISAX=m -CONFIG_ISDN_DRV_ICN=m -CONFIG_ISDN_DRV_PCBIT=m -CONFIG_ISDN_DRV_SC=m -CONFIG_ISDN_DRV_ACT2000=m -CONFIG_ISDN_DRV_TPAM=m -CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y -CONFIG_ISDN_DRV_AVMB1_AVM_CS=m +# +# ISDN4Linux hardware drivers +# -CONFIG_ISDN_CAPI_CAPIDRV=m +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m +# +# D-channel protocol features +# CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y CONFIG_HISAX_1TR6=y CONFIG_HISAX_NI1=y CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# CONFIG_HISAX_16_0=y CONFIG_HISAX_16_3=y CONFIG_HISAX_TELESPCI=y @@ -1182,21 +1298,31 @@ CONFIG_HISAX_W6692=y CONFIG_HISAX_HFC_SX=y CONFIG_HISAX_ENTERNOW_PCI=y # CONFIG_HISAX_DEBUG is not set -CONFIG_HISAX_AVM_A1_CS=m -CONFIG_HISAX_ST5481=m -CONFIG_HISAX_HFCUSB=m -CONFIG_HISAX_FRITZ_PCIPNP=m -CONFIG_HISAX_NO_SENDCOMPLETE=y -CONFIG_HISAX_NO_LLC=y -CONFIG_HISAX_NO_KEYPAD=y + +# +# HiSax PCMCIA card service modules +# CONFIG_HISAX_SEDLBAUER_CS=m CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m CONFIG_HISAX_TELES_CS=m -CONFIG_ISDN_DRV_LOOP=m -CONFIG_HYSDN=m -CONFIG_HYSDN_CAPI=y +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m # # CAPI subsystem @@ -1207,6 +1333,7 @@ CONFIG_ISDN_CAPI_MIDDLEWARE=y CONFIG_ISDN_CAPI_CAPI20=m CONFIG_ISDN_CAPI_CAPIFS_BOOL=y CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m # # CAPI hardware drivers @@ -1254,21 +1381,14 @@ CONFIG_INPUT_EVDEV=y # Input I/O drivers # # CONFIG_GAMEPORT is not set -CONFIG_SOUND_GAMEPORT=m -CONFIG_GAMEPORT_NS558=m -CONFIG_GAMEPORT_L4=m -CONFIG_GAMEPORT_EMU10K1=m -CONFIG_GAMEPORT_VORTEX=m -CONFIG_GAMEPORT_FM801=m -CONFIG_GAMEPORT_CS461x=m +CONFIG_SOUND_GAMEPORT=y CONFIG_SERIO=y CONFIG_SERIO_I8042=y CONFIG_SERIO_SERPORT=y -# CONFIG_SERIO_RAW is not set - # CONFIG_SERIO_CT82C710 is not set # CONFIG_SERIO_PARKBD is not set # CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -1276,9 +1396,9 @@ CONFIG_SERIO_SERPORT=y CONFIG_INPUT_KEYBOARD=y CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_LKKBD is not set CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y CONFIG_MOUSE_SERIAL=m @@ -1288,20 +1408,7 @@ CONFIG_MOUSE_LOGIBM=m CONFIG_MOUSE_PC110PAD=m CONFIG_MOUSE_VSXXXAA=m CONFIG_INPUT_JOYSTICK=y -# CONFIG_JOYSTICK_ANALOG is not set -# CONFIG_JOYSTICK_A3D is not set -# CONFIG_JOYSTICK_ADI is not set -# CONFIG_JOYSTICK_COBRA is not set -# CONFIG_JOYSTICK_GF2K is not set -# CONFIG_JOYSTICK_GRIP is not set -# CONFIG_JOYSTICK_GRIP_MP is not set -# CONFIG_JOYSTICK_GUILLEMOT is not set -# CONFIG_JOYSTICK_INTERACT is not set -# CONFIG_JOYSTICK_SIDEWINDER is not set -# CONFIG_JOYSTICK_TMDC is not set # CONFIG_JOYSTICK_IFORCE is not set -# CONFIG_JOYSTICK_IFORCE_USB=y -# CONFIG_JOYSTICK_IFORCE_232=y # CONFIG_JOYSTICK_WARRIOR is not set # CONFIG_JOYSTICK_MAGELLAN is not set # CONFIG_JOYSTICK_SPACEORB is not set @@ -1311,12 +1418,11 @@ CONFIG_INPUT_JOYSTICK=y # CONFIG_JOYSTICK_DB9 is not set # CONFIG_JOYSTICK_GAMECON is not set # CONFIG_JOYSTICK_TURBOGRAFX is not set -CONFIG_JOYSTICK_JOYDUMP=m CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_GUNZE=m CONFIG_INPUT_MISC=y CONFIG_INPUT_PCSPKR=m -CONFIG_INPUT_UINPUT=m +CONFIG_INPUT_UINPUT=m # # Character devices @@ -1326,12 +1432,11 @@ CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y CONFIG_SERIAL_NONSTANDARD=y # CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set CONFIG_SYNCLINK=m CONFIG_SYNCLINKMP=m CONFIG_N_HDLC=m CONFIG_STALDRV=y -# CONFIG_FTAPE is not set -CONFIG_IBM_ASM=m # # Serial drivers @@ -1347,20 +1452,6 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_8250_DETECT_IRQ=y CONFIG_SERIAL_8250_MULTIPORT=y CONFIG_SERIAL_8250_RSA=y -# CONFIG_COMPUTONE is not set -# CONFIG_CYCLADES is not set -# CONFIG_DIGIEPCA is not set -# CONFIG_DIGI is not set -# CONFIG_ESPSERIAL is not set -# CONFIG_MOXA_INTELLIO is not set -# CONFIG_MOXA_SMARTIO is not set -# CONFIG_ISI is not set -# CONFIG_RISCOM8 is not set -# CONFIG_SPECIALIX is not set -# CONFIG_SX is not set -# CONFIG_RIO is not set -# CONFIG_STALLION is not set -# CONFIG_ISTALLION is not set # # Non-8250 serial port support @@ -1369,104 +1460,20 @@ CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set +CONFIG_CRASH=m CONFIG_PRINTER=m CONFIG_LP_CONSOLE=y CONFIG_PPDEV=m # CONFIG_TIPAR is not set # -# I2C support -# -CONFIG_I2C=m -CONFIG_I2C_CHARDEV=m - -# -# I2C Algorithms -# -# CONFIG_I2C_DEBUG_ALGO is not set -CONFIG_I2C_ALGOBIT=m -CONFIG_I2C_ALGOPCF=m - -# -# I2C Hardware Bus support -# -CONFIG_I2C_ALI1535=m -CONFIG_I2C_ALI15X3=m -CONFIG_I2C_AMD756=m -CONFIG_I2C_AMD8111=m -CONFIG_I2C_I801=m -CONFIG_I2C_I810=m -CONFIG_I2C_ISA=m -CONFIG_I2C_NFORCE2=m -CONFIG_I2C_PIIX4=m -CONFIG_I2C_PROSAVAGE=m -CONFIG_I2C_SAVAGE4=m -# CONFIG_SCx200_ACB is not set -CONFIG_I2C_SIS5595=m -CONFIG_I2C_SIS630=m -CONFIG_I2C_SIS96X=m -CONFIG_I2C_VIA=m -CONFIG_I2C_VIAPRO=m -CONFIG_I2C_VOODOO3=m -# CONFIG_I2C_ELEKTOR is not set -CONFIG_I2C_PARPORT=m -CONFIG_I2C_PARPORT_LIGHT=m -# CONFIG_I2C_DEBUG_CORE is not set -# CONFIG_I2C_DEBUG_BUS is not set -# CONFIG_I2C_DEBUG_CHIP is not set -# CONFIG_I2C_PARPORT is not set -CONFIG_I2C_ALI1563=m -# CONFIG_I2C_PARPORT_LIGHT is not set -CONFIG_I2C_ALGOPCA=m -# CONFIG_I2C_PCA_ISA is not set - - - -# -# I2C Hardware Sensors Chip support -# -CONFIG_I2C_SENSOR=m -CONFIG_SENSORS_ADM1021=m -CONFIG_SENSORS_EEPROM=m -CONFIG_SENSORS_IT87=m -CONFIG_SENSORS_LM75=m -CONFIG_SENSORS_LM78=m -CONFIG_SENSORS_LM80=m -CONFIG_SENSORS_LM83=m -CONFIG_SENSORS_LM85=m -CONFIG_SENSORS_VIA686A=m -CONFIG_SENSORS_W83781D=m -CONFIG_SENSORS_ASB100=m -CONFIG_SENSORS_LM90=m -CONFIG_SENSORS_W83L785TS=m -CONFIG_SENSORS_FSCHER=m -CONFIG_SENSORS_GL518SM=m -CONFIG_SENSORS_DS1621=m -CONFIG_SENSORS_W83627HF=m -CONFIG_SENSORS_PCF8574=m -CONFIG_SENSORS_PCF8591=m -CONFIG_SENSORS_RTC8564=m -CONFIG_SENSORS_MAX1619=m -CONFIG_SENSORS_ADM1025=m -CONFIG_SENSORS_ADM1031=m -CONFIG_SENSORS_LM77=m -CONFIG_SENSORS_SMSC47M1=m - -# CONFIG_W1 is not set - -# -# Mice -# -CONFIG_CRASH=m - -# # IPMI # CONFIG_IPMI_HANDLER=m # CONFIG_IPMI_PANIC_EVENT is not set CONFIG_IPMI_DEVICE_INTERFACE=m -CONFIG_IPMI_WATCHDOG=m CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m CONFIG_IPMI_POWEROFF=m # @@ -1474,33 +1481,47 @@ CONFIG_IPMI_POWEROFF=m # CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# CONFIG_SOFT_WATCHDOG=m -CONFIG_WDT=m -# CONFIG_WDT_501 is not set -CONFIG_WDTPCI=m -CONFIG_WDT_501_PCI=y -CONFIG_PCWATCHDOG=m CONFIG_ACQUIRE_WDT=m CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m CONFIG_EUROTECH_WDT=m CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m CONFIG_I8XX_TCO=m -# CONFIG_MIXCOMWD is not set +CONFIG_SC1200_WDT=m # CONFIG_SCx200_WDT is not set # CONFIG_60XX_WDT is not set -CONFIG_W83877F_WDT=m +CONFIG_CPU5_WDT=m CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m CONFIG_MACHZ_WDT=m -CONFIG_SC520_WDT=m -CONFIG_ALIM7101_WDT=m -CONFIG_ALIM1535_WDT=m -CONFIG_SC1200_WDT=m -CONFIG_WAFER_WDT=m -CONFIG_CPU5_WDT=m -CONFIG_PCIPCWATCHDOG=m -CONFIG_USBPCWATCHDOG=m +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m CONFIG_HW_RANDOM=m CONFIG_NVRAM=m CONFIG_RTC=y @@ -1526,70 +1547,142 @@ CONFIG_AGP_VIA=y CONFIG_AGP_EFFICEON=y CONFIG_DRM=y # CONFIG_DRM_TDFX is not set -# CONFIG_DRM_GAMMA is not set CONFIG_DRM_R128=m CONFIG_DRM_RADEON=m CONFIG_DRM_I810=m CONFIG_DRM_I830=m +CONFIG_DRM_I915=m CONFIG_DRM_MGA=m # CONFIG_DRM_SIS is not set -CONFIG_DRM_I915=m - - # # PCMCIA character devices # CONFIG_SYNCLINK_CS=m - - - # CONFIG_MWAVE is not set CONFIG_RAW_DRIVER=y +# CONFIG_HPET is not set CONFIG_MAX_RAW_DEVS=8192 CONFIG_HANGCHECK_TIMER=m # -# Multimedia devices +# I2C support # -# CONFIG_VIDEO_DEV is not set +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m # -# Video For Linux +# I2C Algorithms # +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m # -# Video Adapters +# I2C Hardware Bus support # -# CONFIG_VIDEO_BT848 is not set -# CONFIG_VIDEO_PMS is not set -# CONFIG_VIDEO_BWQCAM is not set +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_BWQCAM is not set # CONFIG_VIDEO_CQCAM is not set # CONFIG_VIDEO_W9966 is not set # CONFIG_VIDEO_CPIA is not set -# CONFIG_VIDEO_CPIA_PP is not set -# CONFIG_VIDEO_CPIA_USB is not set +# CONFIG_VIDEO_SAA5246A is not set # CONFIG_VIDEO_SAA5249 is not set # CONFIG_TUNER_3036 is not set # CONFIG_VIDEO_STRADIS is not set # CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN_BUZ is not set -# CONFIG_VIDEO_ZORAN_DC10 is not set -# CONFIG_VIDEO_ZORAN_DC30 is not set -# CONFIG_VIDEO_ZORAN_LML33 is not set -# CONFIG_VIDEO_ZORAN_LML33R10 is not set -# CONFIG_VIDEO_MEYE is not set # CONFIG_VIDEO_SAA7134 is not set # CONFIG_VIDEO_MXB is not set # CONFIG_VIDEO_DPC is not set # CONFIG_VIDEO_HEXIUM_ORION is not set # CONFIG_VIDEO_HEXIUM_GEMINI is not set # CONFIG_VIDEO_CX88 is not set -# CONFIG_VIDEO_SAA5246A is not set CONFIG_VIDEO_OVCAMCHIP=m - # # Radio Adapters # @@ -1601,119 +1694,49 @@ CONFIG_VIDEO_OVCAMCHIP=m # CONFIG_RADIO_GEMTEK_PCI is not set # CONFIG_RADIO_MAXIRADIO is not set # CONFIG_RADIO_MAESTRO is not set -# CONFIG_RADIO_MIROPCM20 is not set -# CONFIG_RADIO_MIROPCM20_RDS is not set # CONFIG_RADIO_SF16FMI is not set # CONFIG_RADIO_SF16FMR2 is not set # CONFIG_RADIO_TERRATEC is not set # CONFIG_RADIO_TRUST is not set # CONFIG_RADIO_TYPHOON is not set -# CONFIG_RADIO_TYPHOON_PROC_FS=y # CONFIG_RADIO_ZOLTRIX is not set - # # Digital Video Broadcasting Devices # # CONFIG_DVB is not set -CONFIG_DVB_CORE=m - -# -# Supported Frontend Modules -# -CONFIG_DVB_STV0299=m -# CONFIG_DVB_SP887X is not set -# CONFIG_DVB_ALPS_TDLB7 is not set -CONFIG_DVB_ALPS_TDMB7=m -CONFIG_DVB_ATMEL_AT76C651=m -CONFIG_DVB_CX24110=m -CONFIG_DVB_GRUNDIG_29504_491=m -CONFIG_DVB_GRUNDIG_29504_401=m -CONFIG_DVB_MT312=m -CONFIG_DVB_VES1820=m -CONFIG_DVB_VES1X93=m -CONFIG_DVB_TWINHAN_DST=m -CONFIG_DVB_TTUSB_DEC=m -CONFIG_DVB_BT8XX=m -# CONFIG_DVB_TDA1004X is not set -CONFIG_DVB_NXT6000=m - -# -# Supported SAA7146 based PCI Adapters -# -CONFIG_DVB_AV7110=m -CONFIG_DVB_AV7110_OSD=y -# CONFIG_DVB_AV7110_FIRMWARE is not set -CONFIG_DVB_BUDGET=m -CONFIG_DVB_BUDGET_CI=m -CONFIG_DVB_BUDGET_AV=m -CONFIG_DVB_BUDGET_PATCH=m - -# -# Supported USB Adapters -# -CONFIG_DVB_TTUSB_BUDGET=m - -# -# Supported FlexCopII (B2C2) Adapters -# -CONFIG_DVB_B2C2_SKYSTAR=m -CONFIG_VIDEO_SAA7146=m -CONFIG_VIDEO_SAA7146_VV=m -CONFIG_VIDEO_VIDEOBUF=m -CONFIG_VIDEO_TUNER=m -CONFIG_VIDEO_BUF=m -CONFIG_VIDEO_BTCX=m # # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set CONFIG_FB_VGA16=m CONFIG_FB_VESA=y CONFIG_VIDEO_SELECT=y # CONFIG_FB_HGA is not set CONFIG_FB_RIVA=m -# CONFIG_FB_RIVA_DEBUG is not set # CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set CONFIG_FB_I810=m CONFIG_FB_I810_GTF=y # CONFIG_FB_MATROX is not set -CONFIG_FB_MATROX_MILLENIUM=y -CONFIG_FB_MATROX_MYSTIQUE=y -CONFIG_FB_MATROX_G450=y -CONFIG_FB_MATROX_G100=y -CONFIG_FB_MATROX_I2C=m -CONFIG_FB_MATROX_MAVEN=m -CONFIG_FB_MATROX_MULTIHEAD=y -# CONFIG_FB_RADEON is not set -# CONFIG_FB_RADEON_DEBUG is not set # CONFIG_FB_RADEON_OLD is not set -CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON is not set # CONFIG_FB_ATY128 is not set # CONFIG_FB_ATY is not set -CONFIG_FB_ATY_CT=y -CONFIG_FB_ATY_GX=y -# CONFIG_FB_ATY_XL_INIT is not set # CONFIG_FB_SIS is not set -CONFIG_FB_SIS_300=y -CONFIG_FB_SIS_315=y -# CONFIG_FB_NEOMAGIC is not set # CONFIG_FB_NEOMAGIC is not set +CONFIG_FB_KYRO=m # CONFIG_FB_3DFX is not set # CONFIG_FB_VOODOO1 is not set # CONFIG_FB_TRIDENT is not set # CONFIG_FB_VIRTUAL is not set -CONFIG_FB_KYRO=m -# CONFIG_FB_PM2 is not set -# CONFIG_FB_PM2_FIFO_DISCONNECT is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_HGA_ACCEL is not set -# CONFIG_FB_3DFX_ACCEL is not set -# CONFIG_FB_TRIDENT_ACCEL is not set -CONFIG_FB_CIRRUS=m # # Console display driver support @@ -1723,7 +1746,8 @@ CONFIG_MDA_CONSOLE=m CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y # CONFIG_FONTS is not set - +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y # # Logo configuration @@ -1742,6 +1766,10 @@ CONFIG_SOUND=m # Advanced Linux Sound Architecture # CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m CONFIG_SND_SEQUENCER=m CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_OSSEMUL=y @@ -1751,11 +1779,13 @@ CONFIG_SND_SEQUENCER_OSS=y CONFIG_SND_RTCTIMER=m # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set -CONFIG_SND_BIT32_EMUL=y # # Generic devices # +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_VX_LIB=m CONFIG_SND_DUMMY=m CONFIG_SND_VIRMIDI=m CONFIG_SND_MTPAV=m @@ -1765,12 +1795,10 @@ CONFIG_SND_MPU401=m # # ISA devices # -# CONFIG_SND_AD1816A is not set # CONFIG_SND_AD1848 is not set # CONFIG_SND_CS4231 is not set # CONFIG_SND_CS4232 is not set # CONFIG_SND_CS4236 is not set -# CONFIG_SND_ES968 is not set # CONFIG_SND_ES1688 is not set # CONFIG_SND_ES18XX is not set # CONFIG_SND_GUSCLASSIC is not set @@ -1784,12 +1812,8 @@ CONFIG_SND_MPU401=m # CONFIG_SND_SB8 is not set # CONFIG_SND_SB16 is not set # CONFIG_SND_SBAWE is not set -# CONFIG_SND_SB16_CSP=y # CONFIG_SND_WAVEFRONT is not set -# CONFIG_SND_ALS100 is not set -# CONFIG_SND_AZT2320 is not set # CONFIG_SND_CMI8330 is not set -# CONFIG_SND_DT019X is not set # CONFIG_SND_OPL3SA2 is not set # CONFIG_SND_SGALAXY is not set # CONFIG_SND_SSCAPE is not set @@ -1797,13 +1821,21 @@ CONFIG_SND_MPU401=m # # PCI devices # +CONFIG_SND_AC97_CODEC=m CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m CONFIG_SND_CS46XX=m CONFIG_SND_CS46XX_NEW_DSP=y CONFIG_SND_CS4281=m CONFIG_SND_EMU10K1=m CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m CONFIG_SND_NM256=m CONFIG_SND_RME32=m CONFIG_SND_RME96=m @@ -1819,23 +1851,14 @@ CONFIG_SND_ES1938=m CONFIG_SND_ES1968=m CONFIG_SND_MAESTRO3=m CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m CONFIG_SND_ICE1712=m CONFIG_SND_ICE1724=m CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m CONFIG_SND_SONICVIBES=m CONFIG_SND_VIA82XX=m CONFIG_SND_VX222=m -CONFIG_SND_BT87X=m -CONFIG_SND_ATIIXP=m -CONFIG_SND_ATIIXP_MODEM=m -CONFIG_SND_AU8810=m -CONFIG_SND_AU8820=m -CONFIG_SND_AU8830=m -CONFIG_SND_MIXART=m -CONFIG_SND_FM801_TEA575X=m -CONFIG_SND_INTEL8X0M=m -CONFIG_SND_PDAUDIOCF=m - # # ALSA USB devices @@ -1848,13 +1871,12 @@ CONFIG_SND_USB_USX2Y=m # # CONFIG_SND_VXPOCKET is not set # CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m # # Open Sound System # # CONFIG_SOUND_PRIME is not set -# CONFIG_SOUND_BT878 is not set -# CONFIG_SOUND_CMPCI is not set # # USB support @@ -1869,6 +1891,7 @@ CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers @@ -1890,9 +1913,9 @@ CONFIG_USB_UHCI_HCD=m CONFIG_USB_MIDI=m CONFIG_USB_ACM=m CONFIG_USB_PRINTER=m -# CONFIG_BLK_DEV_UB is not set CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y CONFIG_USB_STORAGE_DATAFAB=y CONFIG_USB_STORAGE_FREECOM=y CONFIG_USB_STORAGE_ISD200=y @@ -1901,7 +1924,6 @@ CONFIG_USB_STORAGE_HP8200e=y CONFIG_USB_STORAGE_SDDR09=y CONFIG_USB_STORAGE_SDDR55=y CONFIG_USB_STORAGE_JUMPSHOT=y -CONFIG_USB_STORAGE_RW_DETECT=y # # USB Human Interface Devices (HID) @@ -1913,17 +1935,14 @@ CONFIG_HID_PID=y CONFIG_LOGITECH_FF=y CONFIG_THRUSTMASTER_FF=y CONFIG_USB_HIDDEV=y - -# -# USB HID Boot Protocol drivers -# -# CONFIG_USB_KBD is not set -# CONFIG_USB_MOUSE is not set CONFIG_USB_AIPTEK=m CONFIG_USB_WACOM=m CONFIG_USB_KBTAB=m CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m # # USB Imaging devices @@ -1942,8 +1961,10 @@ CONFIG_USB_IBMCAM=m CONFIG_USB_KONICAWC=m CONFIG_USB_OV511=m CONFIG_USB_SE401=m -CONFIG_USB_STV680=m CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m # # USB Network adaptors @@ -1953,11 +1974,11 @@ CONFIG_USB_KAWETH=m CONFIG_USB_PEGASUS=m CONFIG_USB_RTL8150=m CONFIG_USB_USBNET=m -CONFIG_USB_SPEEDTOUCH=m # # USB Host-to-Host Cables # +CONFIG_USB_ALI_M5632=y CONFIG_USB_AN2720=y CONFIG_USB_BELKIN=y CONFIG_USB_GENESYS=y @@ -2019,40 +2040,33 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_CYBERJACK=m CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m -CONFIG_USB_SERIAL_WHITEHEAT=m CONFIG_USB_EZUSB=y -CONFIG_USB_EMI62=m -CONFIG_USB_LED=m -CONFIG_USB_G_SERIAL=m - # # USB Miscellaneous drivers # +CONFIG_USB_EMI62=m # CONFIG_USB_EMI26 is not set # CONFIG_USB_TIGL is not set CONFIG_USB_AUERSWALD=m CONFIG_USB_RIO500=m -CONFIG_USB_LCD=m -CONFIG_USB_TEST=m -# CONFIG_USB_GADGET is not set -# CONFIG_USB_GADGET_PXA2XX is not set -# CONFIG_USB_GADGET_GOKU is not set -# CONFIG_USB_GADGET_SA1100 is not set -CONFIG_USB_ZERO=m -CONFIG_USB_ETH=m -CONFIG_USB_GADGETFS=m -CONFIG_USB_W9968CF=m -CONFIG_USB_PWC=m CONFIG_USB_LEGOTOWER=m -CONFIG_USB_FILE_STORAGE=m -# CONFIG_USB_FILE_STORAGE_TEST is not set -CONFIG_USB_MTOUCH=m -CONFIG_USB_ATI_REMOTE=m -CONFIG_USB_ALI_M5632=y +CONFIG_USB_LCD=m +CONFIG_USB_LED=m # CONFIG_USB_CYTHERM is not set -CONFIG_USB_EGALAX=m CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set # # File systems @@ -2069,20 +2083,9 @@ CONFIG_JBD=m # CONFIG_JBD_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set -# CONFIG_REISERFS_CHECK is not set -CONFIG_REISERFS_PROC_INFO=y -CONFIG_REISERFS_FS_XATTR=y -CONFIG_REISERFS_FS_POSIX_ACL=y -CONFIG_REISERFS_FS_SECURITY=y # CONFIG_JFS_FS is not set -# CONFIG_JFS_DEBUG is not set -# CONFIG_JFS_STATISTICS is not set -CONFIG_JFS_POSIX_ACL=y +CONFIG_FS_POSIX_ACL=y # CONFIG_XFS_FS is not set -# CONFIG_XFS_RT is not set -CONFIG_XFS_QUOTA=y -CONFIG_XFS_POSIX_ACL=y -CONFIG_XFS_SECURITY=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_QUOTA=y @@ -2100,6 +2103,7 @@ CONFIG_JOLIET=y CONFIG_ZISOFS=y CONFIG_ZISOFS_FS=y CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -2116,6 +2120,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="ascii" # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set CONFIG_DEVPTS_FS_XATTR=y CONFIG_DEVPTS_FS_SECURITY=y @@ -2129,27 +2134,27 @@ CONFIG_RAMFS=y # # Miscellaneous filesystems # -# CONFIG_ADFS_FS is not seta +# CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set -# uses sleepon and needs a major update CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m # CONFIG_BEFS_FS is not set -# CONFIG_BEFS_DEBUG is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set -# CONFIG_QNX4FS_RW is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_UFS_FS_WRITE is not set # # Network File Systems @@ -2176,17 +2181,8 @@ CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y # CONFIG_NCP_FS is not set -CONFIG_NCPFS_PACKET_SIGNING=y -CONFIG_NCPFS_IOCTL_LOCKING=y -CONFIG_NCPFS_STRONG=y -CONFIG_NCPFS_NFS_NS=y -CONFIG_NCPFS_OS2_NS=y -CONFIG_NCPFS_SMALLDOS=y -CONFIG_NCPFS_NLS=y -CONFIG_NCPFS_EXTRAS=y # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set -# CONFIG_RXRPC is not set # # Partition Types @@ -2207,11 +2203,11 @@ CONFIG_SGI_PARTITION=y # CONFIG_ULTRIX_PARTITION is not set CONFIG_SUN_PARTITION=y CONFIG_EFI_PARTITION=y -CONFIG_NLS=y # # Native Language Support # +CONFIG_NLS=y CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m @@ -2236,6 +2232,7 @@ CONFIG_NLS_CODEPAGE_874=m CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=m CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m @@ -2250,7 +2247,6 @@ CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=m -CONFIG_NLS_ASCII=y # # Profiling support @@ -2259,27 +2255,24 @@ CONFIG_PROFILING=y CONFIG_OPROFILE=m # -# Tux -# -CONFIG_TUX=m -CONFIG_TUX_EXTCGI=y -# CONFIG_TUX_EXTENDED_LOG is not set -# CONFIG_TUX_DEBUG is not set - - -# # Kernel hacking # CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SLAB is not set CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y CONFIG_DEBUG_HIGHMEM=y # CONFIG_DEBUG_INFO is not set -CONFIG_DEBUG_SPINLOCK_SLEEP=y # CONFIG_FRAME_POINTER is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -# CONFIG_DEBUG_DRIVER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y # # Security options @@ -2290,13 +2283,11 @@ CONFIG_SECURITY_CAPABILITIES=y # CONFIG_SECURITY_ROOTPLUG is not set CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SELINUX_DEVELOP=y CONFIG_SECURITY_SELINUX_AVC_STATS=y # CONFIG_SECURITY_SELINUX_MLS is not set -CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 -CONFIG_AUDIT=y -CONFIG_AUDITSYSCALL=y # # Cryptographic options @@ -2309,432 +2300,35 @@ CONFIG_CRYPTO_MD5=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_DES=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_SERPENT=m -CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_AES_586=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m CONFIG_CRYPTO_MICHAEL_MIC=m -# CONFIG_CRYPTO_TEST is not set -CONFIG_LIBCRC32C=m CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y CONFIG_CRYPTO_SIGNATURE_DSA=y CONFIG_CRYPTO_MPILIB=y -CONFIG_CRYPTO_TEA=m -CONFIG_CRYPTO_KHAZAD=m -CONFIG_CRYPTO_WP512=m # # Library routines # -CONFIG_CRC32=m CONFIG_CRC_CCITT=m - +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y CONFIG_PC=y -# CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_INIA100 is not set -# CONFIG_ATALK is not set -# CONFIG_DEV_APPLETALK is not set -# CONFIG_LTPC is not set -# CONFIG_COPS is not set -# CONFIG_IPX is not set -# CONFIG_IPDDP is not set -# CONFIG_IRDA is not set -# CONFIG_NCP_FS is not set -# CONFIG_ISAPNP is not set -# CONFIG_PCMCIA_AHA152X is not set -# CONFIG_PCMCIA_NINJA_SCSI is not set -# CONFIG_PCMCIA_QLOGIC is not set -# CONFIG_IEEE1394 is not set -# CONFIG_EL1 is not set -# CONFIG_EL2 is not set -# CONFIG_ELPLUS is not set -# CONFIG_WD80x3 is not set -# CONFIG_IRDA is not set -# CONFIG_GAMEPORT is not set -# CONFIG_DVB is not set -# CONFIG_SND_AD1816A is not set -# CONFIG_SND_AD1848 is not set -# CONFIG_SND_CS4231 is not set -# CONFIG_SND_CS4232 is not set -# CONFIG_SND_CS4236 is not set -# CONFIG_SND_ES968 is not set -# CONFIG_SND_ES1688 is not set -# CONFIG_SND_ES18XX is not set -# CONFIG_SND_GUSCLASSIC is not set -# CONFIG_SND_GUSEXTREME is not set -# CONFIG_SND_GUSMAX is not set -# CONFIG_SND_INTERWAVE is not set -# CONFIG_SND_INTERWAVE_STB is not set -# CONFIG_SND_OPTI92X_AD1848 is not set -# CONFIG_SND_OPTI92X_CS4231 is not set -# CONFIG_SND_OPTI93X is not set -# CONFIG_SND_SB8 is not set -# CONFIG_SND_SB16 is not set -# CONFIG_SND_SBAWE is not set -# CONFIG_SND_SB16_CSP=y -# CONFIG_SND_WAVEFRONT is not set -# CONFIG_SND_ALS100 is not set -# CONFIG_SND_AZT2320 is not set -# CONFIG_SND_CMI8330 is not set -# CONFIG_SND_DT019X is not set -# CONFIG_SND_OPL3SA2 is not set -# CONFIG_SND_SGALAXY is not set -# CONFIG_SND_SSCAPE is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_XFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set -# CONFIG_WAN_ROUTER is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_BINFMT_AOUT is not set -# CONFIG_DRM_TDFX is not set -# CONFIG_DRM_GAMMA is not set -# CONFIG_DRM_SIS is not set -# CONFIG_BLK_DEV_UMEM is not set -# CONFIG_MWAVE is not set -# CONFIG_ROCKETPORT is not set -# CONFIG_R3964 is not set -# CONFIG_TIPAR is not set -# CONFIG_JOYSTICK_ANALOG is not set -# CONFIG_JOYSTICK_A3D is not set -# CONFIG_JOYSTICK_ADI is not set -# CONFIG_JOYSTICK_COBRA is not set -# CONFIG_JOYSTICK_GF2K is not set -# CONFIG_JOYSTICK_GRIP is not set -# CONFIG_JOYSTICK_GRIP_MP is not set -# CONFIG_JOYSTICK_GUILLEMOT is not set -# CONFIG_JOYSTICK_INTERACT is not set -# CONFIG_JOYSTICK_SIDEWINDER is not set -# CONFIG_JOYSTICK_TMDC is not set -# CONFIG_JOYSTICK_IFORCE is not set -# CONFIG_JOYSTICK_IFORCE_USB=y -# CONFIG_JOYSTICK_IFORCE_232=y -# CONFIG_JOYSTICK_WARRIOR is not set -# CONFIG_JOYSTICK_MAGELLAN is not set -# CONFIG_JOYSTICK_SPACEORB is not set -# CONFIG_JOYSTICK_SPACEBALL is not set -# CONFIG_JOYSTICK_STINGER is not set -# CONFIG_JOYSTICK_TWIDDLER is not set -# CONFIG_JOYSTICK_DB9 is not set -# CONFIG_JOYSTICK_GAMECON is not set -# CONFIG_JOYSTICK_TURBOGRAFX is not set -# CONFIG_RADIO_CADET is not set -# CONFIG_RADIO_RTRACK is not set -# CONFIG_RADIO_RTRACK2 is not set -# CONFIG_RADIO_AZTECH is not set -# CONFIG_RADIO_GEMTEK is not set -# CONFIG_RADIO_GEMTEK_PCI is not set -# CONFIG_RADIO_MAXIRADIO is not set -# CONFIG_RADIO_MAESTRO is not set -# CONFIG_RADIO_MIROPCM20 is not set -# CONFIG_RADIO_MIROPCM20_RDS is not set -# CONFIG_RADIO_SF16FMI is not set -# CONFIG_RADIO_SF16FMR2 is not set -# CONFIG_RADIO_TERRATEC is not set -# CONFIG_RADIO_TRUST is not set -# CONFIG_RADIO_TYPHOON is not set -# CONFIG_RADIO_TYPHOON_PROC_FS=y -# CONFIG_RADIO_ZOLTRIX is not set -# CONFIG_VIDEO_DEV is not set -# CONFIG_PLIP is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_HGA is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_AHA1542 is not set -CONFIG_SCSI_FUTURE_DOMAIN=m -# CONFIG_SCSI_IN2000 is not set -# CONFIG_SCSI_QLOGIC_FAS is not set -# CONFIG_SCSI_QLOGIC_ISP is not set -# CONFIG_VIDEO_BT848 is not set -# CONFIG_VIDEO_PMS is not set -# CONFIG_VIDEO_BWQCAM is not set -# CONFIG_VIDEO_CQCAM is not set -# CONFIG_VIDEO_W9966 is not set -# CONFIG_VIDEO_CPIA is not set -# CONFIG_VIDEO_CPIA_PP is not set -# CONFIG_VIDEO_CPIA_USB is not set -# CONFIG_VIDEO_SAA5249 is not set -# CONFIG_TUNER_3036 is not set -# CONFIG_VIDEO_STRADIS is not set -# CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN_BUZ is not set -# CONFIG_VIDEO_ZORAN_DC10 is not set -# CONFIG_VIDEO_ZORAN_DC30 is not set -# CONFIG_VIDEO_ZORAN_LML33 is not set -# CONFIG_VIDEO_ZORAN_LML33R10 is not set -# CONFIG_VIDEO_MEYE is not set -# CONFIG_VIDEO_SAA7134 is not set -# CONFIG_VIDEO_MXB is not set -# CONFIG_VIDEO_DPC is not set -# CONFIG_VIDEO_HEXIUM_ORION is not set -# CONFIG_VIDEO_HEXIUM_GEMINI is not set -# CONFIG_VIDEO_CX88 is not set -# CONFIG_VIDEO_SAA5246A is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_I82092 is not set -# CONFIG_YELLOWFIN is not set -# CONFIG_SUNDANCE is not set -# CONFIG_ULTRA is not set -# CONFIG_SKFP is not set -# CONFIG_DE600 is not set -# CONFIG_DE620 is not set -# CONFIG_CS89x0 is not set -# CONFIG_DGRS is not set -# CONFIG_AC3200 is not set -# CONFIG_NI52 is not set -# CONFIG_NI65 is not set -# CONFIG_LANCE is not set -# CONFIG_ELPLUS is not set -# CONFIG_EL16 is not set -# CONFIG_EL3 is not set -# CONFIG_3C515 is not set -# CONFIG_HAMACHI is not set -CONFIG_HP100=m -# CONFIG_EQUALIZER is not set -# CONFIG_NET_SB1000 is not set -# CONFIG_DEPCA is not set -# CONFIG_ATP is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_INFTL is not set -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001PLUS is not set -# CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PCI is not set -# CONFIG_PCMCIA_SYM53C500 is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_HGA_ACCEL is not set -# CONFIG_FB_3DFX_ACCEL is not set -# CONFIG_FB_TRIDENT_ACCEL is not set -# CONFIG_SCSI_DC390T is not set -CONFIG_AUDIT=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_I2C_PCA_ISA is not set -CONFIG_RAW_DRIVER=y -# CONFIG_MTD_SCB2_FLASH is not set -CONFIG_UID16=y -# CONFIG_X86_PC is not set -# CONFIG_X86_ELAN is not set -# CONFIG_X86_VOYAGER is not set -# CONFIG_X86_NUMAQ is not set -# CONFIG_X86_SUMMIT is not set -# CONFIG_X86_BIGSMP is not set -# CONFIG_X86_VISWS is not set -CONFIG_X86_GENERICARCH=y -# CONFIG_X86_ES7000 is not set -# CONFIG_M386 is not set -# CONFIG_M486 is not set -# CONFIG_M586 is not set -# CONFIG_M586TSC is not set -# CONFIG_M586MMX is not set -CONFIG_M686=y -# CONFIG_MPENTIUMII is not set -# CONFIG_MPENTIUMIII is not set -# CONFIG_MPENTIUMM is not set -# CONFIG_MPENTIUM4 is not set -# CONFIG_MK6 is not set -# CONFIG_MK7 is not set -# CONFIG_MK8 is not set -# CONFIG_MCRUSOE is not set -# CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set -# CONFIG_MWINCHIP3D is not set -# CONFIG_MCYRIXIII is not set -# CONFIG_MVIAC3_2 is not set -CONFIG_X86_GENERIC=y -CONFIG_X86_CMPXCHG=y -CONFIG_X86_XADD=y -CONFIG_X86_L1_CACHE_SHIFT=7 -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_X86_PPRO_FENCE=y -CONFIG_X86_WP_WORKS_OK=y -CONFIG_X86_INVLPG=y -CONFIG_X86_BSWAP=y -CONFIG_X86_POPAD_OK=y -CONFIG_X86_GOOD_APIC=y -CONFIG_X86_INTEL_USERCOPY=y -CONFIG_X86_USE_PPRO_CHECKSUM=y -# CONFIG_HPET is not set -CONFIG_HPET_TIMER=y -CONFIG_HPET_EMULATE_RTC=y -CONFIG_NR_CPUS=32 -CONFIG_X86_LOCAL_APIC=y -CONFIG_X86_IO_APIC=y -CONFIG_X86_TSC=y -CONFIG_X86_MCE=y -# CONFIG_X86_MCE_NONFATAL is not set -CONFIG_X86_MCE_P4THERMAL=y -CONFIG_TOSHIBA=m -CONFIG_I8K=m -CONFIG_SONYPI=m -CONFIG_MICROCODE=m -CONFIG_X86_MSR=m -CONFIG_X86_CPUID=m -CONFIG_EDD=m -# CONFIG_NUMA is not set -# CONFIG_NOHIGHMEM is not set -CONFIG_HIGHMEM4G=y -CONFIG_HIGHMEM64G=y -# CONFIG_HIGHMEM64G is not set -CONFIG_HIGHMEM=y -CONFIG_HIGHPTE=y -# CONFIG_MATH_EMULATION is not set -CONFIG_MTRR=y -CONFIG_HAVE_DEC_LOCK=y -# CONFIG_X86_UP_APIC is not set -CONFIG_X86_PM_TIMER=y -# CONFIG_X86_4G is not set -# CONFIG_EFI is not set -CONFIG_REGPARM=y -# CONFIG_PCI_GOBIOS is not set -# CONFIG_PCI_GODIRECT is not set -# CONFIG_PCI_GOMMCONFIG is not set -CONFIG_PCI_GOANY=y -CONFIG_MDA_CONSOLE=m -CONFIG_SYNCLINK_CS=m -CONFIG_SYNCLINK=m -CONFIG_SYNCLINKMP=m -CONFIG_HP100=m -CONFIG_PCMCIA_FDOMAIN=m -CONFIG_SCSI_FUTURE_DOMAIN=m -CONFIG_CRASH=m -CONFIG_CAPI_EICON=y -CONFIG_I2O=m -CONFIG_I2O_BLOCK=m -CONFIG_I2O_SCSI=m -CONFIG_I2O_PROC=m -CONFIG_I2O_CONFIG=y -CONFIG_APM=y -# CONFIG_APM_IGNORE_USER_SUSPEND is not set -# CONFIG_APM_DO_ENABLE is not set -CONFIG_APM_CPU_IDLE=y -# CONFIG_APM_DISPLAY_BLANK is not set -CONFIG_APM_RTC_IS_GMT=y -# CONFIG_APM_ALLOW_INTS is not set -# CONFIG_APM_REAL_MODE_POWER_OFF is not set -CONFIG_X86_FIND_SMP_CONFIG=y -CONFIG_X86_MPPARSE=y -CONFIG_ACPI=y -CONFIG_ACPI_BOOT=y -CONFIG_ACPI_INTERPRETER=y -CONFIG_ACPI_SLEEP=y -CONFIG_ACPI_AC=m -CONFIG_ACPI_BATTERY=m -CONFIG_ACPI_BUTTON=m -CONFIG_ACPI_FAN=y -CONFIG_ACPI_PROCESSOR=y -CONFIG_ACPI_THERMAL=y -CONFIG_ACPI_ASUS=m -CONFIG_ACPI_TOSHIBA=m -# CONFIG_ACPI_DEBUG is not set -CONFIG_ACPI_BUS=y -CONFIG_ACPI_EC=y -CONFIG_ACPI_POWER=y -CONFIG_ACPI_PCI=y -CONFIG_ACPI_SYSTEM=y -CONFIG_ACPI_NUMA=y -CONFIG_ACPI_BLACKLIST_YEAR=2001 -CONFIG_X86_ACPI_CPUFREQ=y -# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set -CONFIG_X86_POWERNOW_K6=m -CONFIG_X86_POWERNOW_K7=y -CONFIG_X86_POWERNOW_K8=m -# CONFIG_X86_GX_SUSPMOD is not set -# CONFIG_X86_SPEEDSTEP_CENTRINO is not set -CONFIG_X86_SPEEDSTEP_ICH=y -CONFIG_X86_SPEEDSTEP_SMI=m -CONFIG_X86_SPEEDSTEP_LIB=y -# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set -CONFIG_X86_P4_CLOCKMOD=m -CONFIG_X86_LONGRUN=y -CONFIG_X86_LONGHAUL=y -CONFIG_X86_SMP=y -CONFIG_X86_HT=y -CONFIG_X86_BIOS_REBOOT=y -CONFIG_X86_TRAMPOLINE=y -CONFIG_TUX=m -CONFIG_NVRAM=m -CONFIG_IBM_ASM=m -CONFIG_CRYPTO_AES_586=m -CONFIG_GENERIC_ISA_DMA=y -CONFIG_SCHED_SMT=y -# CONFIG_IRQBALANCE is not set -# CONFIG_SOFTWARE_SUSPEND is not set -# CONFIG_PM_DEBUG is not set -CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_PROC_INTF is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m -CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_ONDEMAND=m -# CONFIG_CPU_FREQ_24_API is not set -CONFIG_CPU_FREQ_TABLE=y -CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_DEBUG_PAGEALLOC is not set -# CONFIG_KPROBES is not set -CONFIG_KEXEC=y -CONFIG_NETDUMP=m -# CONFIG_SCHEDSTATS is not set -CONFIG_PCI_DIRECT=y -CONFIG_PCI_MMCONFIG=y -CONFIG_PCI_BIOS=y -CONFIG_HOTPLUG_PCI=y -# CONFIG_HOTPLUG_PCI_FAKE is not set -CONFIG_HOTPLUG_PCI_COMPAQ=m -# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set -CONFIG_HOTPLUG_PCI_IBM=m -CONFIG_HOTPLUG_PCI_ACPI=m -CONFIG_HOTPLUG_PCI_ACPI_IBM=m -# CONFIG_HOTPLUG_PCI_CPCI is not set -CONFIG_HOTPLUG_PCI_PCIE=m -# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set -CONFIG_HOTPLUG_PCI_SHPC=m -# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set -# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set -CONFIG_PM=y -CONFIG_IEEE80211=m -# CONFIG_IEEE80211_DEBUG is not set -CONFIG_IEEE80211_CRYPT=m -CONFIG_IEEE80211_WPA=m -CONFIG_IEEE80211_CRYPT_CCMP=m -CONFIG_IEEE80211_CRYPT_TKIP=m -CONFIG_IPW2100=m -# CONFIG_IPW_DEBUG is not set -CONFIG_IPW2100_PROMISC=y -# CONFIG_IPW2100_LEGACY_FW_LOAD is not set -CONFIG_IPW2200=m -CONFIG_M686=y -# CONFIG_NOHIGHMEM is not set -CONFIG_HIGHMEM64G=y -CONFIG_HOTPLUG_PCI=y -CONFIG_HOTPLUG_PCI_COMPAQ=m -CONFIG_HOTPLUG_PCI_IBM=m -# CONFIG_HIGHMEM64G is not set -# CONFIG_EWRK3 is not set -CONFIG_NR_CPUS=32 -# CONFIG_X86_PC is not set -CONFIG_X86_GENERICARCH=y diff --git a/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686.config b/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686.config index 8d66567..18cb4d1 100644 --- a/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686.config +++ b/lustre/kernel_patches/kernel_configs/kernel-2.6.9-2.6-rhel4-i686.config @@ -1,28 +1,34 @@ # # Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-prep +# Fri May 13 14:09:31 2005 # +CONFIG_X86=y CONFIG_MMU=y -# CONFIG_SMP is not set -# CONFIG_HOTPLUG_CPU is not set -CONFIG_LOCALVERSION="" +CONFIG_UID16=y +CONFIG_GENERIC_ISA_DMA=y +CONFIG_GENERIC_IOMAP=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y -CONFIG_STANDALONE=y -CONFIG_PREVENT_FIRMWARE_BUILD=y # # General setup # +CONFIG_LOCALVERSION="" CONFIG_SWAP=y CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y CONFIG_BSD_PROCESS_ACCT=y # CONFIG_BSD_PROCESS_ACCT_V3 is not set CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_AUDITSYSCALL=y CONFIG_LOG_BUF_SHIFT=17 +CONFIG_HOTPLUG=y # CONFIG_IKCONFIG is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y @@ -33,9 +39,10 @@ CONFIG_EPOLL=y CONFIG_IOSCHED_NOOP=y CONFIG_IOSCHED_AS=y CONFIG_IOSCHED_DEADLINE=y -# CONFIG_PREEMPT is not set -CONFIG_PREEMPT_VOLUNTARY=y -CONFIG_POSIX_MQUEUE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set # # Loadable module support @@ -45,14 +52,188 @@ CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set CONFIG_OBSOLETE_MODPARM=y CONFIG_MODVERSIONS=y -CONFIG_KMOD=y CONFIG_MODULE_SIG=y # CONFIG_MODULE_SIG_FORCE is not set +CONFIG_KMOD=y +CONFIG_STOP_MACHINE=y + +# +# Processor type and features +# +# CONFIG_X86_PC is not set +# CONFIG_X86_ELAN is not set +# CONFIG_X86_VOYAGER is not set +# CONFIG_X86_NUMAQ is not set +# CONFIG_X86_SUMMIT is not set +# CONFIG_X86_BIGSMP is not set +# CONFIG_X86_VISWS is not set +CONFIG_X86_GENERICARCH=y +# CONFIG_X86_ES7000 is not set +CONFIG_X86_CYCLONE_TIMER=y +# CONFIG_M386 is not set +# CONFIG_M486 is not set +# CONFIG_M586 is not set +# CONFIG_M586TSC is not set +# CONFIG_M586MMX is not set +CONFIG_M686=y +# CONFIG_MPENTIUMII is not set +# CONFIG_MPENTIUMIII is not set +# CONFIG_MPENTIUMM is not set +# CONFIG_MPENTIUM4 is not set +# CONFIG_MK6 is not set +# CONFIG_MK7 is not set +# CONFIG_MK8 is not set +# CONFIG_MCRUSOE is not set +# CONFIG_MWINCHIPC6 is not set +# CONFIG_MWINCHIP2 is not set +# CONFIG_MWINCHIP3D is not set +# CONFIG_MCYRIXIII is not set +# CONFIG_MVIAC3_2 is not set +CONFIG_X86_GENERIC=y +CONFIG_X86_CMPXCHG=y +CONFIG_X86_XADD=y +CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_X86_PPRO_FENCE=y +CONFIG_X86_WP_WORKS_OK=y +CONFIG_X86_INVLPG=y +CONFIG_X86_BSWAP=y +CONFIG_X86_POPAD_OK=y +CONFIG_X86_GOOD_APIC=y +CONFIG_X86_INTEL_USERCOPY=y +CONFIG_X86_USE_PPRO_CHECKSUM=y +# CONFIG_X86_4G is not set +# CONFIG_X86_SWITCH_PAGETABLES is not set +# CONFIG_X86_4G_VM_LAYOUT is not set +# CONFIG_X86_UACCESS_INDIRECT is not set +# CONFIG_X86_HIGH_ENTRY is not set +CONFIG_HPET_TIMER=y +CONFIG_HPET_EMULATE_RTC=y +CONFIG_SMP=y +CONFIG_NR_CPUS=32 +CONFIG_SCHED_SMT=y +# CONFIG_PREEMPT is not set +CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_X86_LOCAL_APIC=y +CONFIG_X86_IO_APIC=y +CONFIG_X86_TSC=y +CONFIG_X86_MCE=y +# CONFIG_X86_MCE_NONFATAL is not set +CONFIG_X86_MCE_P4THERMAL=y +CONFIG_TOSHIBA=m +CONFIG_I8K=m +CONFIG_MICROCODE=m +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m + +# +# Firmware Drivers +# +CONFIG_EDD=m +# CONFIG_NOHIGHMEM is not set +# CONFIG_HIGHMEM4G is not set +CONFIG_HIGHMEM64G=y +CONFIG_HIGHMEM=y +CONFIG_X86_PAE=y +# CONFIG_NUMA is not set +CONFIG_HIGHPTE=y +# CONFIG_MATH_EMULATION is not set +CONFIG_MTRR=y +# CONFIG_EFI is not set +# CONFIG_IRQBALANCE is not set +CONFIG_HAVE_DEC_LOCK=y +CONFIG_REGPARM=y +CONFIG_KEXEC=y + +# +# Power management options (ACPI, APM) +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SOFTWARE_SUSPEND is not set + +# +# ACPI (Advanced Configuration and Power Interface) Support +# +CONFIG_ACPI=y +CONFIG_ACPI_BOOT=y +CONFIG_ACPI_INTERPRETER=y +CONFIG_ACPI_SLEEP=y +CONFIG_ACPI_SLEEP_PROC_FS=y +CONFIG_ACPI_AC=m +CONFIG_ACPI_BATTERY=m +CONFIG_ACPI_BUTTON=m +CONFIG_ACPI_FAN=y +CONFIG_ACPI_PROCESSOR=y +CONFIG_ACPI_THERMAL=y +CONFIG_ACPI_ASUS=m +CONFIG_ACPI_TOSHIBA=m +CONFIG_ACPI_BLACKLIST_YEAR=2001 +# CONFIG_ACPI_DEBUG is not set +CONFIG_ACPI_BUS=y +CONFIG_ACPI_EC=y +CONFIG_ACPI_POWER=y +CONFIG_ACPI_PCI=y +CONFIG_ACPI_SYSTEM=y +CONFIG_X86_PM_TIMER=y + +# +# APM (Advanced Power Management) BIOS Support +# +CONFIG_APM=y +# CONFIG_APM_IGNORE_USER_SUSPEND is not set +# CONFIG_APM_DO_ENABLE is not set +CONFIG_APM_CPU_IDLE=y +# CONFIG_APM_DISPLAY_BLANK is not set +CONFIG_APM_RTC_IS_GMT=y +# CONFIG_APM_ALLOW_INTS is not set +# CONFIG_APM_REAL_MODE_POWER_OFF is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +# CONFIG_CPU_FREQ_PROC_INTF is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=y +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +# CONFIG_CPU_FREQ_24_API is not set +CONFIG_CPU_FREQ_TABLE=y + +# +# CPUFreq processor drivers +# +CONFIG_X86_ACPI_CPUFREQ=y +# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set +CONFIG_X86_POWERNOW_K6=m +CONFIG_X86_POWERNOW_K7=y +CONFIG_X86_POWERNOW_K7_ACPI=y +CONFIG_X86_POWERNOW_K8=m +CONFIG_X86_POWERNOW_K8_ACPI=y +# CONFIG_X86_GX_SUSPMOD is not set +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set +CONFIG_X86_SPEEDSTEP_ICH=y +CONFIG_X86_SPEEDSTEP_SMI=m +CONFIG_X86_P4_CLOCKMOD=m +CONFIG_X86_SPEEDSTEP_LIB=y +# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set +CONFIG_X86_LONGRUN=y +CONFIG_X86_LONGHAUL=y # # Bus options (PCI, PCMCIA, EISA, MCA, ISA) # CONFIG_PCI=y +# CONFIG_PCI_GOBIOS is not set +# CONFIG_PCI_GOMMCONFIG is not set +# CONFIG_PCI_GODIRECT is not set +CONFIG_PCI_GOANY=y +CONFIG_PCI_BIOS=y +CONFIG_PCI_DIRECT=y +CONFIG_PCI_MMCONFIG=y CONFIG_PCI_MSI=y CONFIG_PCI_LEGACY_PROC=y # CONFIG_PCI_NAMES is not set @@ -60,22 +241,37 @@ CONFIG_ISA=y # CONFIG_EISA is not set # CONFIG_MCA is not set # CONFIG_SCx200 is not set -CONFIG_HOTPLUG=y # # PCMCIA/CardBus support # CONFIG_PCMCIA=m # CONFIG_PCMCIA_DEBUG is not set -CONFIG_YENTA=y +CONFIG_YENTA=m CONFIG_CARDBUS=y +CONFIG_PD6729=m # CONFIG_I82092 is not set CONFIG_I82365=m -CONFIG_PD6729=m CONFIG_TCIC=m CONFIG_PCMCIA_PROBE=y # +# PCI Hotplug Support +# +CONFIG_HOTPLUG_PCI=y +# CONFIG_HOTPLUG_PCI_FAKE is not set +CONFIG_HOTPLUG_PCI_COMPAQ=m +# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set +CONFIG_HOTPLUG_PCI_IBM=m +CONFIG_HOTPLUG_PCI_ACPI=m +CONFIG_HOTPLUG_PCI_ACPI_IBM=m +# CONFIG_HOTPLUG_PCI_CPCI is not set +CONFIG_HOTPLUG_PCI_PCIE=m +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set +CONFIG_HOTPLUG_PCI_SHPC=m +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set + +# # Executable file formats # CONFIG_BINFMT_ELF=y @@ -89,18 +285,23 @@ CONFIG_BINFMT_MISC=y # # Generic Driver Options # +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set # # Memory Technology Devices (MTD) # CONFIG_MTD=m # CONFIG_MTD_DEBUG is not set -CONFIG_MTD_PARTITIONS=m +CONFIG_MTD_PARTITIONS=y CONFIG_MTD_CONCAT=m CONFIG_MTD_REDBOOT_PARTS=m +# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set +# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set CONFIG_MTD_CMDLINE_PARTS=y -CONFIG_MTD_PARTITIONS=y + # # User Modules And Translation Layers # @@ -119,13 +320,24 @@ CONFIG_MTD_CFI=m CONFIG_MTD_JEDECPROBE=m CONFIG_MTD_GEN_PROBE=m # CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set CONFIG_MTD_CFI_INTELEXT=m CONFIG_MTD_CFI_AMDSTD=m +CONFIG_MTD_CFI_AMDSTD_RETRY=3 CONFIG_MTD_CFI_STAA=m +CONFIG_MTD_CFI_UTIL=m CONFIG_MTD_RAM=m CONFIG_MTD_ROM=m CONFIG_MTD_ABSENT=m -# CONFIG_MTD_OBSOLETE_CHIPS is not set # # Mapping drivers for chip access @@ -135,23 +347,19 @@ CONFIG_MTD_ABSENT=m # CONFIG_MTD_PNC2000 is not set CONFIG_MTD_SC520CDP=m CONFIG_MTD_NETSC520=m -CONFIG_MTD_SBC_GXX=m -CONFIG_MTD_ELAN_104NC=m CONFIG_MTD_SCx200_DOCFLASH=m # CONFIG_MTD_AMD76XROM is not set # CONFIG_MTD_SCB2_FLASH is not set # CONFIG_MTD_NETtel is not set # CONFIG_MTD_DILNETPC is not set # CONFIG_MTD_L440GX is not set -# CONFIG_MTD_PCI is not set # # Self-contained MTD device drivers # # CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_PMC551_BUGFIX is not set -# CONFIG_MTD_PMC551_DEBUG is not set # CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set CONFIG_MTD_MTDRAM=m CONFIG_MTDRAM_TOTAL_SIZE=4096 CONFIG_MTDRAM_ERASE_SIZE=128 @@ -163,9 +371,6 @@ CONFIG_MTDRAM_ERASE_SIZE=128 # CONFIG_MTD_DOC2000 is not set # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set -CONFIG_MTD_DOCPROBE=m -# CONFIG_MTD_DOCPROBE_ADVANCED is not set -CONFIG_MTD_DOCPROBE_ADDRESS=0 # # NAND Flash Device Drivers @@ -173,15 +378,7 @@ CONFIG_MTD_DOCPROBE_ADDRESS=0 CONFIG_MTD_NAND=m # CONFIG_MTD_NAND_VERIFY_WRITE is not set CONFIG_MTD_NAND_IDS=m - -# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set -# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set -CONFIG_MTD_CFI_AMDSTD_RETRY=3 -# CONFIG_MTD_ICHXROM is not set -# CONFIG_MTD_PHRAM is not set # CONFIG_MTD_NAND_DISKONCHIP is not set -# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set - # # Parallel port support @@ -222,45 +419,41 @@ CONFIG_BLK_DEV_DAC960=m CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_CRYPTOLOOP=m CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_SX8=m +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_SIZE=16384 CONFIG_BLK_DEV_INITRD=y -CONFIG_BLK_DEV_ATIIXP=y -CONFIG_BLK_DEV_DELKIN=y -CONFIG_BLK_DEV_IT8212=y CONFIG_LBD=y -# CONFIG_DCSSBLK is not set - +CONFIG_DISKDUMP=m # # ATA/ATAPI/MFM/RLL support # CONFIG_IDE=y -CONFIG_IDE_GENERIC=y -# CONFIG_HPT34X_AUTODMA is not set - CONFIG_BLK_DEV_IDE=y # # Please see Documentation/ide.txt for help/info on IDE drives # +# CONFIG_BLK_DEV_IDE_SATA is not set # CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y CONFIG_IDEDISK_MULTI_MODE=y CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_DELKIN=m CONFIG_BLK_DEV_IDECD=y # CONFIG_BLK_DEV_IDETAPE is not set CONFIG_BLK_DEV_IDEFLOPPY=y CONFIG_BLK_DEV_IDESCSI=m # CONFIG_IDE_TASK_IOCTL is not set # CONFIG_IDE_TASKFILE_IO is not set -# CONFIG_BLK_DEV_IDE_SATA is not set # # IDE chipset support/bugfixes # +CONFIG_IDE_GENERIC=y # CONFIG_BLK_DEV_CMD640 is not set -# CONFIG_BLK_DEV_CMD640_ENHANCED is not set CONFIG_BLK_DEV_IDEPNP=y CONFIG_BLK_DEV_IDEPCI=y CONFIG_IDEPCI_SHARE_IRQ=y @@ -276,15 +469,18 @@ CONFIG_BLK_DEV_AEC62XX=y CONFIG_BLK_DEV_ALI15X3=y # CONFIG_WDC_ALI15X3 is not set CONFIG_BLK_DEV_AMD74XX=y +CONFIG_BLK_DEV_ATIIXP=y CONFIG_BLK_DEV_CMD64X=y CONFIG_BLK_DEV_TRIFLEX=y CONFIG_BLK_DEV_CY82C693=y CONFIG_BLK_DEV_CS5520=y CONFIG_BLK_DEV_CS5530=y CONFIG_BLK_DEV_HPT34X=y +# CONFIG_HPT34X_AUTODMA is not set CONFIG_BLK_DEV_HPT366=y # CONFIG_BLK_DEV_SC1200 is not set CONFIG_BLK_DEV_PIIX=y +CONFIG_BLK_DEV_IT8212=y # CONFIG_BLK_DEV_NS87415 is not set CONFIG_BLK_DEV_PDC202XX_OLD=y # CONFIG_PDC202XX_BURST is not set @@ -296,6 +492,7 @@ CONFIG_BLK_DEV_SIS5513=y CONFIG_BLK_DEV_SLC90E66=y # CONFIG_BLK_DEV_TRM290 is not set CONFIG_BLK_DEV_VIA82CXXX=y +# CONFIG_IDE_ARM is not set # CONFIG_IDE_CHIPSETS is not set CONFIG_BLK_DEV_IDEDMA=y # CONFIG_IDEDMA_IVB is not set @@ -312,6 +509,8 @@ CONFIG_SCSI_PROC_FS=y # SCSI support type (disk, tape, CD-ROM) # CONFIG_BLK_DEV_SD=m +CONFIG_SCSI_DUMP=m +CONFIG_SD_IOSTATS=y CONFIG_CHR_DEV_ST=m CONFIG_CHR_DEV_OSST=m CONFIG_BLK_DEV_SR=m @@ -324,8 +523,12 @@ CONFIG_CHR_DEV_SG=m # CONFIG_SCSI_MULTI_LUN is not set CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y -CONFIG_SCSI_SPI_ATTRS=y -CONFIG_SCSI_FC_ATTRS=y + +# +# SCSI Transport Attributes +# +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m # # SCSI low-level drivers @@ -340,8 +543,6 @@ CONFIG_SCSI_AACRAID=m CONFIG_SCSI_AIC7XXX=m CONFIG_AIC7XXX_CMDS_PER_DEVICE=4 CONFIG_AIC7XXX_RESET_DELAY_MS=15000 -# CONFIG_AIC7XXX_PROBE_EISA_VL is not set -# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set # CONFIG_AIC7XXX_DEBUG_ENABLE is not set CONFIG_AIC7XXX_DEBUG_MASK=0 # CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set @@ -349,42 +550,38 @@ CONFIG_SCSI_AIC7XXX_OLD=m CONFIG_SCSI_AIC79XX=m CONFIG_AIC79XX_CMDS_PER_DEVICE=4 CONFIG_AIC79XX_RESET_DELAY_MS=15000 -# CONFIG_AIC79XX_BUILD_FIRMWARE is not set # CONFIG_AIC79XX_ENABLE_RD_STRM is not set # CONFIG_AIC79XX_DEBUG_ENABLE is not set CONFIG_AIC79XX_DEBUG_MASK=0 # CONFIG_AIC79XX_REG_PRETTY_PRINT is not set -# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_DPT_I2O is not set # CONFIG_SCSI_IN2000 is not set CONFIG_MEGARAID_NEWGEN=y CONFIG_MEGARAID_MM=m CONFIG_MEGARAID_MAILBOX=m CONFIG_SCSI_SATA=y +CONFIG_SCSI_SATA_AHCI=m CONFIG_SCSI_SATA_SVW=m CONFIG_SCSI_ATA_PIIX=m +CONFIG_SCSI_SATA_NV=m CONFIG_SCSI_SATA_PROMISE=m -CONFIG_SCSI_SATA_VIA=m -CONFIG_BLK_DEV_SX8=m -CONFIG_SCSI_SATA_VITESSE=m +CONFIG_SCSI_SATA_SX4=m CONFIG_SCSI_SATA_SIL=m CONFIG_SCSI_SATA_SIS=m -CONFIG_SCSI_SATA_SX4=m -CONFIG_SCSI_SATA_NV=m -CONFIG_SCSI_SATA_AHCI=m - +CONFIG_SCSI_SATA_VIA=m +CONFIG_SCSI_SATA_VITESSE=m # CONFIG_SCSI_BUSLOGIC is not set -CONFIG_SCSI_INITIO=m -# CONFIG_SCSI_OMIT_FLASHPOINT is not set -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set # CONFIG_SCSI_DTC3280 is not set # CONFIG_SCSI_EATA is not set # CONFIG_SCSI_EATA_PIO is not set +CONFIG_SCSI_LPFC=m CONFIG_SCSI_FUTURE_DOMAIN=m CONFIG_SCSI_GDTH=m # CONFIG_SCSI_GENERIC_NCR5380 is not set # CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set CONFIG_SCSI_IPS=m +CONFIG_SCSI_INITIO=m # CONFIG_SCSI_INIA100 is not set CONFIG_SCSI_PPA=m CONFIG_SCSI_IMM=m @@ -396,21 +593,13 @@ CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 # CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +# CONFIG_SCSI_IPR is not set # CONFIG_SCSI_PAS16 is not set # CONFIG_SCSI_PSI240I is not set # CONFIG_SCSI_QLOGIC_FAS is not set # CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set -# CONFIG_SCSI_QLOGIC_FC_FIRMWARE is not set CONFIG_SCSI_QLOGIC_1280=m -# CONFIG_SCSI_SYM53C416 is not set -# CONFIG_SCSI_DC395x is not set -# CONFIG_SCSI_T128 is not set -# CONFIG_SCSI_U14_34F is not set -# CONFIG_SCSI_ULTRASTOR is not set -# CONFIG_SCSI_NSP32 is not set -# CONFIG_SCSI_DEBUG is not set -# CONFIG_SCSI_DC390T is not set CONFIG_SCSI_QLA2XXX=m CONFIG_SCSI_QLA21XX=m CONFIG_SCSI_QLA22XX=m @@ -418,10 +607,14 @@ CONFIG_SCSI_QLA2300=m CONFIG_SCSI_QLA2322=m CONFIG_SCSI_QLA6312=m CONFIG_SCSI_QLA6322=m -# CONFIG_SCSI_IPR is not set -# CONFIG_SCSI_DPT_I2O is not set - -CONFIG_SCSI_LPFC=m +# CONFIG_SCSI_SYM53C416 is not set +# CONFIG_SCSI_DC395x is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set # # PCMCIA SCSI adapter support @@ -432,7 +625,6 @@ CONFIG_PCMCIA_FDOMAIN=m # CONFIG_PCMCIA_QLOGIC is not set # CONFIG_PCMCIA_SYM53C500 is not set - # # Old CD-ROM drivers (not SCSI, not IDE) # @@ -446,15 +638,15 @@ CONFIG_BLK_DEV_MD=y CONFIG_MD_LINEAR=m CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m CONFIG_MD_RAID5=m CONFIG_MD_RAID6=m -CONFIG_MD_RAID10=m CONFIG_MD_MULTIPATH=m CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m CONFIG_DM_SNAPSHOT=m CONFIG_DM_MIRROR=m CONFIG_DM_ZERO=m -CONFIG_DM_CRYPT=m # # Fusion MPT device support @@ -465,39 +657,18 @@ CONFIG_FUSION_CTL=m CONFIG_FUSION_LAN=m # -# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# IEEE 1394 (FireWire) support # # CONFIG_IEEE1394 is not set # -# Subsystem Options -# -# CONFIG_IEEE1394_VERBOSEDEBUG is not set -CONFIG_IEEE1394_OUI_DB=y - -# -# Device Drivers -# -# CONFIG_IEEE1394_PCILYNX is not set -CONFIG_IEEE1394_OHCI1394=m - -# -# Protocol Drivers -# -CONFIG_IEEE1394_VIDEO1394=m -CONFIG_IEEE1394_SBP2=m -# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set -# CONFIG_IEEE1394_ETH1394 is not set -CONFIG_IEEE1394_DV1394=m -CONFIG_IEEE1394_RAWIO=m -CONFIG_IEEE1394_CMP=m -CONFIG_IEEE1394_AMDTP=m -# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set - -# # I2O device support # CONFIG_I2O=m +CONFIG_I2O_CONFIG=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m # # Networking support @@ -513,7 +684,6 @@ CONFIG_NETLINK_DEV=y CONFIG_UNIX=y CONFIG_NET_KEY=m CONFIG_INET=y -CONFIG_INET_TUNNEL=m CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y CONFIG_IP_MULTIPLE_TABLES=y @@ -532,14 +702,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=m CONFIG_INET_ESP=m CONFIG_INET_IPCOMP=m -CONFIG_NETCONSOLE=m -# CONFIG_NETPOLL_RX is not set -CONFIG_NETPOLL_TRAP=y -CONFIG_NET_POLL_CONTROLLER=y -CONFIG_NETDUMP=m -CONFIG_DISKDUMP=m -CONFIG_SCSI_DUMP=m -CONFIG_SD_IOSTATS=y +CONFIG_INET_TUNNEL=m # # IP: Virtual Server Configuration @@ -547,10 +710,18 @@ CONFIG_SD_IOSTATS=y CONFIG_IP_VS=m # CONFIG_IP_VS_DEBUG is not set CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# CONFIG_IP_VS_PROTO_TCP=y CONFIG_IP_VS_PROTO_UDP=y CONFIG_IP_VS_PROTO_ESP=y CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# CONFIG_IP_VS_RR=m CONFIG_IP_VS_WRR=m CONFIG_IP_VS_LC=m @@ -561,17 +732,18 @@ CONFIG_IP_VS_DH=m CONFIG_IP_VS_SH=m CONFIG_IP_VS_SED=m CONFIG_IP_VS_NQ=m -CONFIG_IP_VS_FTP=m - +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m CONFIG_IPV6=m CONFIG_IPV6_PRIVACY=y CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m CONFIG_IPV6_TUNNEL=m -# CONFIG_DECNET is not set -CONFIG_BRIDGE=m CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set CONFIG_BRIDGE_NETFILTER=y @@ -580,14 +752,14 @@ CONFIG_BRIDGE_NETFILTER=y # IP: Netfilter Configuration # CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +CONFIG_IP_NF_CT_PROTO_SCTP=m CONFIG_IP_NF_FTP=m CONFIG_IP_NF_IRC=m CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_RAW=m CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_COMMENT=m CONFIG_IP_NF_MATCH_LIMIT=m CONFIG_IP_NF_MATCH_IPRANGE=m CONFIG_IP_NF_MATCH_MAC=m @@ -610,15 +782,18 @@ CONFIG_IP_NF_MATCH_PHYSDEV=m CONFIG_IP_NF_MATCH_ADDRTYPE=m CONFIG_IP_NF_MATCH_REALM=m CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m CONFIG_IP_NF_FILTER=m CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m CONFIG_IP_NF_NAT=m CONFIG_IP_NF_NAT_NEEDED=y CONFIG_IP_NF_TARGET_MASQUERADE=m CONFIG_IP_NF_TARGET_REDIRECT=m CONFIG_IP_NF_TARGET_NETMAP=m CONFIG_IP_NF_TARGET_SAME=m -CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_NAT_LOCAL=y CONFIG_IP_NF_NAT_SNMP_BASIC=m CONFIG_IP_NF_NAT_IRC=m @@ -631,14 +806,11 @@ CONFIG_IP_NF_TARGET_ECN=m CONFIG_IP_NF_TARGET_DSCP=m CONFIG_IP_NF_TARGET_MARK=m CONFIG_IP_NF_TARGET_CLASSIFY=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m CONFIG_IP_NF_ARPTABLES=m CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARP_MANGLE=m -CONFIG_IP_NF_CT_ACCT=y -CONFIG_IP_NF_CT_PROTO_SCTP=m # CONFIG_IP_NF_COMPAT_IPCHAINS is not set # CONFIG_IP_NF_COMPAT_IPFWADM is not set @@ -667,7 +839,6 @@ CONFIG_IP6_NF_MANGLE=m CONFIG_IP6_NF_TARGET_MARK=m CONFIG_IP6_NF_RAW=m - # # Bridge: Netfilter Configuration # @@ -703,33 +874,36 @@ CONFIG_IP_SCTP=m # CONFIG_SCTP_HMAC_SHA1 is not set CONFIG_SCTP_HMAC_MD5=y CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m CONFIG_VLAN_8021Q=m -CONFIG_LLC=m +# CONFIG_DECNET is not set +CONFIG_LLC=y # CONFIG_LLC2 is not set # CONFIG_IPX is not set -# CONFIG_IPX_INTERN is not set # CONFIG_ATALK is not set -# CONFIG_DEV_APPLETALK is not set -# CONFIG_LTPC is not set -# CONFIG_COPS is not set -CONFIG_COPS_DAYNA=y -CONFIG_COPS_TANGENT=y -# CONFIG_IPDDP is not set -CONFIG_IPDDP_ENCAP=y -CONFIG_IPDDP_DECAP=y # CONFIG_X25 is not set # CONFIG_LAPB is not set CONFIG_NET_DIVERT=y # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set -# CONFIG_NET_HW_FLOWCONTROL is not set # # QoS and/or fair queueing # CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m @@ -737,9 +911,8 @@ CONFIG_NET_SCH_TEQL=m CONFIG_NET_SCH_TBF=m CONFIG_NET_SCH_GRED=m CONFIG_NET_SCH_DSMARK=m -CONFIG_NET_SCH_INGRESS=m -CONFIG_NET_SCH_HFSC=m CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m CONFIG_NET_QOS=y CONFIG_NET_ESTIMATOR=y CONFIG_NET_CLS=y @@ -748,24 +921,59 @@ CONFIG_NET_CLS_ROUTE4=m CONFIG_NET_CLS_ROUTE=y CONFIG_NET_CLS_FW=m CONFIG_NET_CLS_U32=m -CONFIG_NET_CLS_RSVP=m -CONFIG_NET_CLS_RSVP6=m -CONFIG_NET_CLS_POLICE=y -# CONFIG_NET_ACT_POLICE is not set CONFIG_CLS_U32_PERF=y CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m # CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y # # Network testing # # CONFIG_NET_PKTGEN is not set -CONFIG_NETDEVICES=y +CONFIG_NETPOLL=y +# CONFIG_NETPOLL_RX is not set +CONFIG_NETPOLL_TRAP=y +CONFIG_NET_POLL_CONTROLLER=y +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=m +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_CMTP=m +CONFIG_BT_HIDP=m # -# ARCnet devices +# Bluetooth device drivers # -# CONFIG_ARCNET is not set +CONFIG_BT_HCIUSB=m +CONFIG_BT_HCIUSB_SCO=y +CONFIG_BT_HCIUART=m +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_BCSP_TXCRC=y +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBFUSB=m +CONFIG_BT_HCIDTL1=m +CONFIG_BT_HCIBT3C=m +CONFIG_BT_HCIBLUECARD=m +CONFIG_BT_HCIBTUART=m +CONFIG_BT_HCIVHCI=m +CONFIG_TUX=m + +# +# TUX options +# +CONFIG_TUX_EXTCGI=y +# CONFIG_TUX_EXTENDED_LOG is not set +# CONFIG_TUX_DEBUG is not set +CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_BONDING=m # CONFIG_EQUALIZER is not set @@ -774,46 +982,9 @@ CONFIG_ETHERTAP=m # CONFIG_NET_SB1000 is not set # -# ATM -# -CONFIG_ATM_CLIP=m -CONFIG_ATM_LANE=m -CONFIG_ATM_BR2684=m -CONFIG_NET_SCH_ATM=m -CONFIG_ATM_TCP=m -CONFIG_ATM_LANAI=m -CONFIG_ATM_ENI=m -CONFIG_ATM_FIRESTREAM=m -# CONFIG_ATM_ZATM is not set -CONFIG_ATM_IDT77252=m -CONFIG_ATM_AMBASSADOR=m -CONFIG_ATM_HORIZON=m -CONFIG_ATM_FORE200E_MAYBE=m -CONFIG_ATM_HE=m -CONFIG_PPPOATM=m -CONFIG_ATM_NICSTAR=m -# CONFIG_ATM_IA is not set - - -# CONFIG_ATM_CLIP_NO_ICMP is not set -# CONFIG_ATM_MPOA is not set -# CONFIG_ATM_BR2684_IPFILTER is not set -# CONFIG_ATM_ENI_DEBUG is not set -# CONFIG_ATM_ENI_TUNE_BURST is not set -# CONFIG_ATM_ZATM_DEBUG is not set -# CONFIG_ATM_IDT77252_DEBUG is not set -# CONFIG_ATM_IDT77252_RCV_ALL is not set -# CONFIG_ATM_AMBASSADOR_DEBUG is not set -# CONFIG_ATM_HORIZON_DEBUG is not set -# CONFIG_ATM_FORE200E_PCA is not set -# CONFIG_ATM_HE_USE_SUNI is not set -# CONFIG_ATM_NICSTAR_USE_SUNI is not set -# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set -# CONFIG_ATM_IA_DEBUG is not set - - - - +# ARCnet devices +# +# CONFIG_ARCNET is not set # # Ethernet (10 or 100Mbit) @@ -826,7 +997,6 @@ CONFIG_NET_VENDOR_3COM=y # CONFIG_EL1 is not set # CONFIG_EL2 is not set # CONFIG_ELPLUS is not set -# CONFIG_ELPLUS is not set # CONFIG_EL16 is not set # CONFIG_EL3 is not set # CONFIG_3C515 is not set @@ -847,12 +1017,9 @@ CONFIG_NET_VENDOR_RACAL=y CONFIG_NET_TULIP=y CONFIG_DE2104X=m CONFIG_TULIP=m -# CONFIG_TULIP_NAPI is not set - # CONFIG_TULIP_MWI is not set CONFIG_TULIP_MMIO=y -# CONFIG_NI5010 is not set -# CONFIG_PCMCIA_XIRTULIP is not set +# CONFIG_TULIP_NAPI is not set CONFIG_DE4X5=m CONFIG_WINBOND_840=m CONFIG_DM9102=m @@ -861,20 +1028,6 @@ CONFIG_PCMCIA_XIRCOM=m # CONFIG_DEPCA is not set CONFIG_HP100=m # CONFIG_NET_ISA is not set -CONFIG_EWRK3=m -CONFIG_E2100=m -CONFIG_EEXPRESS=m -CONFIG_EEXPRESS_PRO=m -CONFIG_HPLAN_PLUS=m -CONFIG_HPLAN=m -CONFIG_LP486E=m -CONFIG_ETH16I=m -CONFIG_NE2000=m -CONFIG_ZNET=m -CONFIG_SEEQ8005=m -CONFIG_LNE390=m -CONFIG_NE3210=m -CONFIG_ES3210=m CONFIG_NET_PCI=y CONFIG_PCNET32=m CONFIG_AMD8111_ETH=m @@ -884,6 +1037,7 @@ CONFIG_ADAPTEC_STARFIRE_NAPI=y # CONFIG_AC3200 is not set CONFIG_APRICOT=m CONFIG_B44=m +CONFIG_FORCEDETH=m # CONFIG_CS89x0 is not set # CONFIG_DGRS is not set CONFIG_EEPRO100=m @@ -891,7 +1045,6 @@ CONFIG_EEPRO100=m CONFIG_E100=m CONFIG_E100_NAPI=y CONFIG_FEALNX=m -CONFIG_FORCEDETH=m CONFIG_NATSEMI=m CONFIG_NE2K_PCI=m CONFIG_8139CP=m @@ -903,11 +1056,9 @@ CONFIG_8139TOO_8129=y CONFIG_SIS900=m CONFIG_EPIC100=m # CONFIG_SUNDANCE is not set -# CONFIG_SUNDANCE_MMIO is not set CONFIG_TLAN=m CONFIG_VIA_RHINE=m CONFIG_VIA_RHINE_MMIO=y -CONFIG_VIA_VELOCITY=m CONFIG_NET_POCKET=y # CONFIG_ATP is not set # CONFIG_DE600 is not set @@ -927,6 +1078,7 @@ CONFIG_NS83820=m CONFIG_R8169=m CONFIG_R8169_NAPI=y CONFIG_SK98LIN=m +CONFIG_VIA_VELOCITY=m CONFIG_TIGON3=m # @@ -936,21 +1088,21 @@ CONFIG_IXGB=m CONFIG_IXGB_NAPI=y CONFIG_S2IO=m CONFIG_S2IO_NAPI=y -CONFIG_FDDI=y -# CONFIG_DEFXX is not set -# CONFIG_SKFP is not set -# CONFIG_HIPPI is not set -# CONFIG_PLIP is not set -CONFIG_PPP=m -CONFIG_PPP_MULTILINK=y -CONFIG_PPP_FILTER=y -CONFIG_PPP_ASYNC=m -CONFIG_PPP_SYNC_TTY=m -CONFIG_PPP_DEFLATE=m -CONFIG_IPPP_FILTER=y -# CONFIG_PPP_BSDCOMP is not set -CONFIG_PPPOE=m -# CONFIG_SLIP is not set + +# +# Token Ring devices +# +CONFIG_TR=y +CONFIG_IBMTR=m +CONFIG_IBMOL=m +CONFIG_IBMLS=m +CONFIG_3C359=m +CONFIG_TMS380TR=m +CONFIG_TMSPCI=m +CONFIG_SKISA=m +CONFIG_PROTEON=m +CONFIG_ABYSS=m +CONFIG_SMCTR=m # # Wireless LAN (non-hamradio) @@ -992,7 +1144,6 @@ CONFIG_TMD_HERMES=m CONFIG_PCI_HERMES=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m -CONFIG_PRISM54=m # # Wireless 802.11b Pcmcia/Cardbus cards support @@ -1001,32 +1152,12 @@ CONFIG_PCMCIA_HERMES=m CONFIG_AIRO_CS=m CONFIG_PCMCIA_ATMEL=m CONFIG_PCMCIA_WL3501=m -CONFIG_NET_WIRELESS=y - -# -# Token Ring devices -# -CONFIG_TR=y -CONFIG_IBMOL=m -CONFIG_3C359=m -CONFIG_TMS380TR=m -CONFIG_TMSPCI=m -CONFIG_ABYSS=m -CONFIG_IBMTR=m -CONFIG_IBMLS=m -CONFIG_SKISA=m -CONFIG_PROTEON=m -CONFIG_SMCTR=m -CONFIG_PCMCIA_IBMTR=m - - -CONFIG_NET_FC=y -# CONFIG_SHAPER is not set # -# Wan interfaces +# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support # -# CONFIG_WAN is not set +CONFIG_PRISM54=m +CONFIG_NET_WIRELESS=y # # PCMCIA network device support @@ -1040,119 +1171,104 @@ CONFIG_PCMCIA_NMCLAN=m CONFIG_PCMCIA_SMC91C92=m CONFIG_PCMCIA_XIRC2PS=m CONFIG_PCMCIA_AXNET=m +CONFIG_PCMCIA_IBMTR=m # -# Amateur Radio support -# -# CONFIG_HAMRADIO is not set - -# -# IrDA (infrared) support -# -# CONFIG_IRDA is not set -# CONFIG_IRDA is not set -# CONFIG_IRDA_DEBUG is not set -CONFIG_IRLAN=m -CONFIG_IRNET=m -CONFIG_IRCOMM=m -# CONFIG_IRDA_ULTRA is not set -CONFIG_IRDA_CACHE_LAST_LSAP=y -CONFIG_IRDA_FAST_RR=y -CONFIG_IRTTY_SIR=m -CONFIG_DONGLE=y -CONFIG_ESI_DONGLE=m -CONFIG_ACTISYS_DONGLE=m -CONFIG_TEKRAM_DONGLE=m -CONFIG_IRPORT_SIR=m -# CONFIG_DONGLE_OLD is not set -CONFIG_LITELINK_DONGLE=m -CONFIG_MA600_DONGLE=m -CONFIG_GIRBIL_DONGLE=m -CONFIG_MCP2120_DONGLE=m -CONFIG_OLD_BELKIN_DONGLE=m -CONFIG_ACT200L_DONGLE=m - -CONFIG_USB_IRDA=m -CONFIG_NSC_FIR=m -CONFIG_SIGMATEL_FIR=m -# CONFIG_WINBOND_FIR is not set -# CONFIG_TOSHIBA_FIR is not set -# CONFIG_SMC_IRCC_FIR is not set -# CONFIG_ALI_FIR is not set -# CONFIG_VLSI_FIR is not set -# CONFIG_VIA_FIR is not set - - - -# -# Bluetooth support +# Wan interfaces # -CONFIG_BT=m -CONFIG_BT_L2CAP=m -CONFIG_BT_SCO=m -CONFIG_BT_CMTP=m -CONFIG_BT_RFCOMM=m -CONFIG_BT_RFCOMM_TTY=y -CONFIG_BT_BNEP=m -CONFIG_BT_BNEP_MC_FILTER=y -CONFIG_BT_BNEP_PROTO_FILTER=y -CONFIG_BT_HIDP=m +# CONFIG_WAN is not set # -# Bluetooth device drivers +# ATM drivers # -CONFIG_BT_HCIUSB=m -CONFIG_BT_HCIUART=m -CONFIG_BT_HCIUART_H4=y -CONFIG_BT_HCIUART_BCSP=y -CONFIG_BT_HCIUART_BCSP_TXCRC=y -CONFIG_BT_HCIDTL1=m -CONFIG_BT_HCIBT3C=m -CONFIG_BT_HCIBLUECARD=m -CONFIG_BT_HCIBTUART=m -CONFIG_BT_HCIVHCI=m -CONFIG_BT_HCIUSB_SCO=y -CONFIG_BT_HCIBCM203X=m -CONFIG_BT_HCIBFUSB=m -CONFIG_USB_BLUETOOTH_TTY=m - +CONFIG_ATM_TCP=m +CONFIG_ATM_LANAI=m +CONFIG_ATM_ENI=m +# CONFIG_ATM_ENI_DEBUG is not set +# CONFIG_ATM_ENI_TUNE_BURST is not set +CONFIG_ATM_FIRESTREAM=m +# CONFIG_ATM_ZATM is not set +CONFIG_ATM_NICSTAR=m +# CONFIG_ATM_NICSTAR_USE_SUNI is not set +# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set +CONFIG_ATM_IDT77252=m +# CONFIG_ATM_IDT77252_DEBUG is not set +# CONFIG_ATM_IDT77252_RCV_ALL is not set +CONFIG_ATM_IDT77252_USE_SUNI=y +CONFIG_ATM_AMBASSADOR=m +# CONFIG_ATM_AMBASSADOR_DEBUG is not set +CONFIG_ATM_HORIZON=m +# CONFIG_ATM_HORIZON_DEBUG is not set +# CONFIG_ATM_IA is not set +CONFIG_ATM_FORE200E_MAYBE=m +# CONFIG_ATM_FORE200E_PCA is not set +CONFIG_ATM_HE=m +# CONFIG_ATM_HE_USE_SUNI is not set +CONFIG_FDDI=y +# CONFIG_DEFXX is not set +# CONFIG_SKFP is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +# CONFIG_PPP_BSDCOMP is not set +CONFIG_PPPOE=m +CONFIG_PPPOATM=m +# CONFIG_SLIP is not set +CONFIG_NET_FC=y +# CONFIG_SHAPER is not set +CONFIG_NETCONSOLE=m +CONFIG_NETDUMP=m + # # ISDN subsystem # - CONFIG_ISDN=m -CONFIG_ISDN_I4L=m -CONFIG_ISDN_DRV_AVMB1_B1ISA=m -CONFIG_ISDN_DRV_AVMB1_B1PCI=m -CONFIG_ISDN_DRV_AVMB1_T1ISA=m -CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m -CONFIG_ISDN_DRV_AVMB1_T1PCI=m -CONFIG_ISDN_DRV_AVMB1_C4=m +# +# Old ISDN4Linux +# +CONFIG_ISDN_I4L=m CONFIG_ISDN_PPP=y CONFIG_ISDN_PPP_VJ=y CONFIG_ISDN_MPP=y +CONFIG_IPPP_FILTER=y # CONFIG_ISDN_PPP_BSDCOMP is not set +CONFIG_ISDN_AUDIO=y CONFIG_ISDN_TTY_FAX=y -CONFIG_DE_AOC=y -CONFIG_ISDN_AUDIO=y +# +# ISDN feature submodules +# -CONFIG_ISDN_DRV_HISAX=m -CONFIG_ISDN_DRV_ICN=m -CONFIG_ISDN_DRV_PCBIT=m -CONFIG_ISDN_DRV_SC=m -CONFIG_ISDN_DRV_ACT2000=m -CONFIG_ISDN_DRV_TPAM=m -CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y -CONFIG_ISDN_DRV_AVMB1_AVM_CS=m +# +# ISDN4Linux hardware drivers +# -CONFIG_ISDN_CAPI_CAPIDRV=m +# +# Passive cards +# +CONFIG_ISDN_DRV_HISAX=m +# +# D-channel protocol features +# CONFIG_HISAX_EURO=y +CONFIG_DE_AOC=y +CONFIG_HISAX_NO_SENDCOMPLETE=y +CONFIG_HISAX_NO_LLC=y +CONFIG_HISAX_NO_KEYPAD=y CONFIG_HISAX_1TR6=y CONFIG_HISAX_NI1=y CONFIG_HISAX_MAX_CARDS=8 + +# +# HiSax supported cards +# CONFIG_HISAX_16_0=y CONFIG_HISAX_16_3=y CONFIG_HISAX_TELESPCI=y @@ -1182,21 +1298,31 @@ CONFIG_HISAX_W6692=y CONFIG_HISAX_HFC_SX=y CONFIG_HISAX_ENTERNOW_PCI=y # CONFIG_HISAX_DEBUG is not set -CONFIG_HISAX_AVM_A1_CS=m -CONFIG_HISAX_ST5481=m -CONFIG_HISAX_HFCUSB=m -CONFIG_HISAX_FRITZ_PCIPNP=m -CONFIG_HISAX_NO_SENDCOMPLETE=y -CONFIG_HISAX_NO_LLC=y -CONFIG_HISAX_NO_KEYPAD=y + +# +# HiSax PCMCIA card service modules +# CONFIG_HISAX_SEDLBAUER_CS=m CONFIG_HISAX_ELSA_CS=m +CONFIG_HISAX_AVM_A1_CS=m CONFIG_HISAX_TELES_CS=m -CONFIG_ISDN_DRV_LOOP=m -CONFIG_HYSDN=m -CONFIG_HYSDN_CAPI=y +# +# HiSax sub driver modules +# +CONFIG_HISAX_ST5481=m +CONFIG_HISAX_HFCUSB=m +CONFIG_HISAX_FRITZ_PCIPNP=m +CONFIG_HISAX_HDLC=y +# +# Active cards +# +CONFIG_ISDN_DRV_ICN=m +CONFIG_ISDN_DRV_PCBIT=m +CONFIG_ISDN_DRV_SC=m +CONFIG_ISDN_DRV_ACT2000=m +CONFIG_ISDN_DRV_TPAM=m # # CAPI subsystem @@ -1207,6 +1333,7 @@ CONFIG_ISDN_CAPI_MIDDLEWARE=y CONFIG_ISDN_CAPI_CAPI20=m CONFIG_ISDN_CAPI_CAPIFS_BOOL=y CONFIG_ISDN_CAPI_CAPIFS=m +CONFIG_ISDN_CAPI_CAPIDRV=m # # CAPI hardware drivers @@ -1254,21 +1381,14 @@ CONFIG_INPUT_EVDEV=y # Input I/O drivers # # CONFIG_GAMEPORT is not set -CONFIG_SOUND_GAMEPORT=m -CONFIG_GAMEPORT_NS558=m -CONFIG_GAMEPORT_L4=m -CONFIG_GAMEPORT_EMU10K1=m -CONFIG_GAMEPORT_VORTEX=m -CONFIG_GAMEPORT_FM801=m -CONFIG_GAMEPORT_CS461x=m +CONFIG_SOUND_GAMEPORT=y CONFIG_SERIO=y CONFIG_SERIO_I8042=y CONFIG_SERIO_SERPORT=y -# CONFIG_SERIO_RAW is not set - # CONFIG_SERIO_CT82C710 is not set # CONFIG_SERIO_PARKBD is not set # CONFIG_SERIO_PCIPS2 is not set +# CONFIG_SERIO_RAW is not set # # Input Device Drivers @@ -1276,9 +1396,9 @@ CONFIG_SERIO_SERPORT=y CONFIG_INPUT_KEYBOARD=y CONFIG_KEYBOARD_ATKBD=y # CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set -# CONFIG_KEYBOARD_LKKBD is not set CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y CONFIG_MOUSE_SERIAL=m @@ -1288,20 +1408,7 @@ CONFIG_MOUSE_LOGIBM=m CONFIG_MOUSE_PC110PAD=m CONFIG_MOUSE_VSXXXAA=m CONFIG_INPUT_JOYSTICK=y -# CONFIG_JOYSTICK_ANALOG is not set -# CONFIG_JOYSTICK_A3D is not set -# CONFIG_JOYSTICK_ADI is not set -# CONFIG_JOYSTICK_COBRA is not set -# CONFIG_JOYSTICK_GF2K is not set -# CONFIG_JOYSTICK_GRIP is not set -# CONFIG_JOYSTICK_GRIP_MP is not set -# CONFIG_JOYSTICK_GUILLEMOT is not set -# CONFIG_JOYSTICK_INTERACT is not set -# CONFIG_JOYSTICK_SIDEWINDER is not set -# CONFIG_JOYSTICK_TMDC is not set # CONFIG_JOYSTICK_IFORCE is not set -# CONFIG_JOYSTICK_IFORCE_USB=y -# CONFIG_JOYSTICK_IFORCE_232=y # CONFIG_JOYSTICK_WARRIOR is not set # CONFIG_JOYSTICK_MAGELLAN is not set # CONFIG_JOYSTICK_SPACEORB is not set @@ -1311,12 +1418,11 @@ CONFIG_INPUT_JOYSTICK=y # CONFIG_JOYSTICK_DB9 is not set # CONFIG_JOYSTICK_GAMECON is not set # CONFIG_JOYSTICK_TURBOGRAFX is not set -CONFIG_JOYSTICK_JOYDUMP=m CONFIG_INPUT_TOUCHSCREEN=y CONFIG_TOUCHSCREEN_GUNZE=m CONFIG_INPUT_MISC=y CONFIG_INPUT_PCSPKR=m -CONFIG_INPUT_UINPUT=m +CONFIG_INPUT_UINPUT=m # # Character devices @@ -1326,12 +1432,11 @@ CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y CONFIG_SERIAL_NONSTANDARD=y # CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set CONFIG_SYNCLINK=m CONFIG_SYNCLINKMP=m CONFIG_N_HDLC=m CONFIG_STALDRV=y -# CONFIG_FTAPE is not set -CONFIG_IBM_ASM=m # # Serial drivers @@ -1347,20 +1452,6 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_8250_DETECT_IRQ=y CONFIG_SERIAL_8250_MULTIPORT=y CONFIG_SERIAL_8250_RSA=y -# CONFIG_COMPUTONE is not set -# CONFIG_CYCLADES is not set -# CONFIG_DIGIEPCA is not set -# CONFIG_DIGI is not set -# CONFIG_ESPSERIAL is not set -# CONFIG_MOXA_INTELLIO is not set -# CONFIG_MOXA_SMARTIO is not set -# CONFIG_ISI is not set -# CONFIG_RISCOM8 is not set -# CONFIG_SPECIALIX is not set -# CONFIG_SX is not set -# CONFIG_RIO is not set -# CONFIG_STALLION is not set -# CONFIG_ISTALLION is not set # # Non-8250 serial port support @@ -1369,104 +1460,20 @@ CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y # CONFIG_LEGACY_PTYS is not set +CONFIG_CRASH=m CONFIG_PRINTER=m CONFIG_LP_CONSOLE=y CONFIG_PPDEV=m # CONFIG_TIPAR is not set # -# I2C support -# -CONFIG_I2C=m -CONFIG_I2C_CHARDEV=m - -# -# I2C Algorithms -# -# CONFIG_I2C_DEBUG_ALGO is not set -CONFIG_I2C_ALGOBIT=m -CONFIG_I2C_ALGOPCF=m - -# -# I2C Hardware Bus support -# -CONFIG_I2C_ALI1535=m -CONFIG_I2C_ALI15X3=m -CONFIG_I2C_AMD756=m -CONFIG_I2C_AMD8111=m -CONFIG_I2C_I801=m -CONFIG_I2C_I810=m -CONFIG_I2C_ISA=m -CONFIG_I2C_NFORCE2=m -CONFIG_I2C_PIIX4=m -CONFIG_I2C_PROSAVAGE=m -CONFIG_I2C_SAVAGE4=m -# CONFIG_SCx200_ACB is not set -CONFIG_I2C_SIS5595=m -CONFIG_I2C_SIS630=m -CONFIG_I2C_SIS96X=m -CONFIG_I2C_VIA=m -CONFIG_I2C_VIAPRO=m -CONFIG_I2C_VOODOO3=m -# CONFIG_I2C_ELEKTOR is not set -CONFIG_I2C_PARPORT=m -CONFIG_I2C_PARPORT_LIGHT=m -# CONFIG_I2C_DEBUG_CORE is not set -# CONFIG_I2C_DEBUG_BUS is not set -# CONFIG_I2C_DEBUG_CHIP is not set -# CONFIG_I2C_PARPORT is not set -CONFIG_I2C_ALI1563=m -# CONFIG_I2C_PARPORT_LIGHT is not set -CONFIG_I2C_ALGOPCA=m -# CONFIG_I2C_PCA_ISA is not set - - - -# -# I2C Hardware Sensors Chip support -# -CONFIG_I2C_SENSOR=m -CONFIG_SENSORS_ADM1021=m -CONFIG_SENSORS_EEPROM=m -CONFIG_SENSORS_IT87=m -CONFIG_SENSORS_LM75=m -CONFIG_SENSORS_LM78=m -CONFIG_SENSORS_LM80=m -CONFIG_SENSORS_LM83=m -CONFIG_SENSORS_LM85=m -CONFIG_SENSORS_VIA686A=m -CONFIG_SENSORS_W83781D=m -CONFIG_SENSORS_ASB100=m -CONFIG_SENSORS_LM90=m -CONFIG_SENSORS_W83L785TS=m -CONFIG_SENSORS_FSCHER=m -CONFIG_SENSORS_GL518SM=m -CONFIG_SENSORS_DS1621=m -CONFIG_SENSORS_W83627HF=m -CONFIG_SENSORS_PCF8574=m -CONFIG_SENSORS_PCF8591=m -CONFIG_SENSORS_RTC8564=m -CONFIG_SENSORS_MAX1619=m -CONFIG_SENSORS_ADM1025=m -CONFIG_SENSORS_ADM1031=m -CONFIG_SENSORS_LM77=m -CONFIG_SENSORS_SMSC47M1=m - -# CONFIG_W1 is not set - -# -# Mice -# -CONFIG_CRASH=m - -# # IPMI # CONFIG_IPMI_HANDLER=m # CONFIG_IPMI_PANIC_EVENT is not set CONFIG_IPMI_DEVICE_INTERFACE=m -CONFIG_IPMI_WATCHDOG=m CONFIG_IPMI_SI=m +CONFIG_IPMI_WATCHDOG=m CONFIG_IPMI_POWEROFF=m # @@ -1474,33 +1481,47 @@ CONFIG_IPMI_POWEROFF=m # CONFIG_WATCHDOG=y # CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# CONFIG_SOFT_WATCHDOG=m -CONFIG_WDT=m -# CONFIG_WDT_501 is not set -CONFIG_WDTPCI=m -CONFIG_WDT_501_PCI=y -CONFIG_PCWATCHDOG=m CONFIG_ACQUIRE_WDT=m CONFIG_ADVANTECH_WDT=m +CONFIG_ALIM1535_WDT=m +CONFIG_ALIM7101_WDT=m +CONFIG_SC520_WDT=m CONFIG_EUROTECH_WDT=m CONFIG_IB700_WDT=m +CONFIG_WAFER_WDT=m CONFIG_I8XX_TCO=m -# CONFIG_MIXCOMWD is not set +CONFIG_SC1200_WDT=m # CONFIG_SCx200_WDT is not set # CONFIG_60XX_WDT is not set -CONFIG_W83877F_WDT=m +CONFIG_CPU5_WDT=m CONFIG_W83627HF_WDT=m +CONFIG_W83877F_WDT=m CONFIG_MACHZ_WDT=m -CONFIG_SC520_WDT=m -CONFIG_ALIM7101_WDT=m -CONFIG_ALIM1535_WDT=m -CONFIG_SC1200_WDT=m -CONFIG_WAFER_WDT=m -CONFIG_CPU5_WDT=m -CONFIG_PCIPCWATCHDOG=m -CONFIG_USBPCWATCHDOG=m +# +# ISA-based Watchdog Cards +# +CONFIG_PCWATCHDOG=m +# CONFIG_MIXCOMWD is not set +CONFIG_WDT=m +# CONFIG_WDT_501 is not set + +# +# PCI-based Watchdog Cards +# +CONFIG_PCIPCWATCHDOG=m +CONFIG_WDTPCI=m +CONFIG_WDT_501_PCI=y +# +# USB-based Watchdog Cards +# +CONFIG_USBPCWATCHDOG=m CONFIG_HW_RANDOM=m CONFIG_NVRAM=m CONFIG_RTC=y @@ -1526,70 +1547,142 @@ CONFIG_AGP_VIA=y CONFIG_AGP_EFFICEON=y CONFIG_DRM=y # CONFIG_DRM_TDFX is not set -# CONFIG_DRM_GAMMA is not set CONFIG_DRM_R128=m CONFIG_DRM_RADEON=m CONFIG_DRM_I810=m CONFIG_DRM_I830=m +CONFIG_DRM_I915=m CONFIG_DRM_MGA=m # CONFIG_DRM_SIS is not set -CONFIG_DRM_I915=m - - # # PCMCIA character devices # CONFIG_SYNCLINK_CS=m - - - # CONFIG_MWAVE is not set CONFIG_RAW_DRIVER=y +# CONFIG_HPET is not set CONFIG_MAX_RAW_DEVS=8192 CONFIG_HANGCHECK_TIMER=m # -# Multimedia devices +# I2C support # -# CONFIG_VIDEO_DEV is not set +CONFIG_I2C=m +CONFIG_I2C_CHARDEV=m # -# Video For Linux +# I2C Algorithms # +CONFIG_I2C_ALGOBIT=m +CONFIG_I2C_ALGOPCF=m +CONFIG_I2C_ALGOPCA=m # -# Video Adapters +# I2C Hardware Bus support # -# CONFIG_VIDEO_BT848 is not set -# CONFIG_VIDEO_PMS is not set -# CONFIG_VIDEO_BWQCAM is not set -# CONFIG_VIDEO_CQCAM is not set -# CONFIG_VIDEO_W9966 is not set -# CONFIG_VIDEO_CPIA is not set -# CONFIG_VIDEO_CPIA_PP is not set -# CONFIG_VIDEO_CPIA_USB is not set -# CONFIG_VIDEO_SAA5249 is not set -# CONFIG_TUNER_3036 is not set -# CONFIG_VIDEO_STRADIS is not set -# CONFIG_VIDEO_ZORAN is not set +CONFIG_I2C_ALI1535=m +CONFIG_I2C_ALI1563=m +CONFIG_I2C_ALI15X3=m +CONFIG_I2C_AMD756=m +CONFIG_I2C_AMD8111=m +CONFIG_I2C_I801=m +CONFIG_I2C_I810=m +CONFIG_I2C_ISA=m +CONFIG_I2C_NFORCE2=m +# CONFIG_I2C_PARPORT is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PIIX4=m +CONFIG_I2C_PROSAVAGE=m +CONFIG_I2C_SAVAGE4=m +# CONFIG_SCx200_ACB is not set +CONFIG_I2C_SIS5595=m +CONFIG_I2C_SIS630=m +CONFIG_I2C_SIS96X=m +CONFIG_I2C_VIA=m +CONFIG_I2C_VIAPRO=m +CONFIG_I2C_VOODOO3=m +# CONFIG_I2C_PCA_ISA is not set + +# +# Hardware Sensors Chip support +# +CONFIG_I2C_SENSOR=m +CONFIG_SENSORS_ADM1021=m +CONFIG_SENSORS_ADM1025=m +CONFIG_SENSORS_ADM1031=m +CONFIG_SENSORS_ASB100=m +CONFIG_SENSORS_DS1621=m +CONFIG_SENSORS_FSCHER=m +CONFIG_SENSORS_GL518SM=m +CONFIG_SENSORS_IT87=m +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM77=m +CONFIG_SENSORS_LM78=m +CONFIG_SENSORS_LM80=m +CONFIG_SENSORS_LM83=m +CONFIG_SENSORS_LM85=m +CONFIG_SENSORS_LM90=m +CONFIG_SENSORS_MAX1619=m +CONFIG_SENSORS_SMSC47M1=m +CONFIG_SENSORS_VIA686A=m +CONFIG_SENSORS_W83781D=m +CONFIG_SENSORS_W83L785TS=m +CONFIG_SENSORS_W83627HF=m + +# +# Other I2C Chip support +# +CONFIG_SENSORS_EEPROM=m +CONFIG_SENSORS_PCF8574=m +CONFIG_SENSORS_PCF8591=m +CONFIG_SENSORS_RTC8564=m +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Misc devices +# +CONFIG_IBM_ASM=m + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=m + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_BT848 is not set +# CONFIG_VIDEO_PMS is not set +# CONFIG_VIDEO_BWQCAM is not set +# CONFIG_VIDEO_CQCAM is not set +# CONFIG_VIDEO_W9966 is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_TUNER_3036 is not set +# CONFIG_VIDEO_STRADIS is not set # CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN_BUZ is not set -# CONFIG_VIDEO_ZORAN_DC10 is not set -# CONFIG_VIDEO_ZORAN_DC30 is not set -# CONFIG_VIDEO_ZORAN_LML33 is not set -# CONFIG_VIDEO_ZORAN_LML33R10 is not set -# CONFIG_VIDEO_MEYE is not set # CONFIG_VIDEO_SAA7134 is not set # CONFIG_VIDEO_MXB is not set # CONFIG_VIDEO_DPC is not set # CONFIG_VIDEO_HEXIUM_ORION is not set # CONFIG_VIDEO_HEXIUM_GEMINI is not set # CONFIG_VIDEO_CX88 is not set -# CONFIG_VIDEO_SAA5246A is not set CONFIG_VIDEO_OVCAMCHIP=m - # # Radio Adapters # @@ -1601,119 +1694,49 @@ CONFIG_VIDEO_OVCAMCHIP=m # CONFIG_RADIO_GEMTEK_PCI is not set # CONFIG_RADIO_MAXIRADIO is not set # CONFIG_RADIO_MAESTRO is not set -# CONFIG_RADIO_MIROPCM20 is not set -# CONFIG_RADIO_MIROPCM20_RDS is not set # CONFIG_RADIO_SF16FMI is not set # CONFIG_RADIO_SF16FMR2 is not set # CONFIG_RADIO_TERRATEC is not set # CONFIG_RADIO_TRUST is not set # CONFIG_RADIO_TYPHOON is not set -# CONFIG_RADIO_TYPHOON_PROC_FS=y # CONFIG_RADIO_ZOLTRIX is not set - # # Digital Video Broadcasting Devices # # CONFIG_DVB is not set -CONFIG_DVB_CORE=m - -# -# Supported Frontend Modules -# -CONFIG_DVB_STV0299=m -# CONFIG_DVB_SP887X is not set -# CONFIG_DVB_ALPS_TDLB7 is not set -CONFIG_DVB_ALPS_TDMB7=m -CONFIG_DVB_ATMEL_AT76C651=m -CONFIG_DVB_CX24110=m -CONFIG_DVB_GRUNDIG_29504_491=m -CONFIG_DVB_GRUNDIG_29504_401=m -CONFIG_DVB_MT312=m -CONFIG_DVB_VES1820=m -CONFIG_DVB_VES1X93=m -CONFIG_DVB_TWINHAN_DST=m -CONFIG_DVB_TTUSB_DEC=m -CONFIG_DVB_BT8XX=m -# CONFIG_DVB_TDA1004X is not set -CONFIG_DVB_NXT6000=m - -# -# Supported SAA7146 based PCI Adapters -# -CONFIG_DVB_AV7110=m -CONFIG_DVB_AV7110_OSD=y -# CONFIG_DVB_AV7110_FIRMWARE is not set -CONFIG_DVB_BUDGET=m -CONFIG_DVB_BUDGET_CI=m -CONFIG_DVB_BUDGET_AV=m -CONFIG_DVB_BUDGET_PATCH=m - -# -# Supported USB Adapters -# -CONFIG_DVB_TTUSB_BUDGET=m - -# -# Supported FlexCopII (B2C2) Adapters -# -CONFIG_DVB_B2C2_SKYSTAR=m -CONFIG_VIDEO_SAA7146=m -CONFIG_VIDEO_SAA7146_VV=m -CONFIG_VIDEO_VIDEOBUF=m -CONFIG_VIDEO_TUNER=m -CONFIG_VIDEO_BUF=m -CONFIG_VIDEO_BTCX=m # # Graphics support # CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_CIRRUS=m +# CONFIG_FB_PM2 is not set # CONFIG_FB_CYBER2000 is not set +# CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set CONFIG_FB_VGA16=m CONFIG_FB_VESA=y CONFIG_VIDEO_SELECT=y # CONFIG_FB_HGA is not set CONFIG_FB_RIVA=m -# CONFIG_FB_RIVA_DEBUG is not set # CONFIG_FB_RIVA_I2C is not set +# CONFIG_FB_RIVA_DEBUG is not set CONFIG_FB_I810=m CONFIG_FB_I810_GTF=y # CONFIG_FB_MATROX is not set -CONFIG_FB_MATROX_MILLENIUM=y -CONFIG_FB_MATROX_MYSTIQUE=y -CONFIG_FB_MATROX_G450=y -CONFIG_FB_MATROX_G100=y -CONFIG_FB_MATROX_I2C=m -CONFIG_FB_MATROX_MAVEN=m -CONFIG_FB_MATROX_MULTIHEAD=y -# CONFIG_FB_RADEON is not set -# CONFIG_FB_RADEON_DEBUG is not set # CONFIG_FB_RADEON_OLD is not set -CONFIG_FB_RADEON_I2C=y +# CONFIG_FB_RADEON is not set # CONFIG_FB_ATY128 is not set # CONFIG_FB_ATY is not set -CONFIG_FB_ATY_CT=y -CONFIG_FB_ATY_GX=y -# CONFIG_FB_ATY_XL_INIT is not set # CONFIG_FB_SIS is not set -CONFIG_FB_SIS_300=y -CONFIG_FB_SIS_315=y -# CONFIG_FB_NEOMAGIC is not set # CONFIG_FB_NEOMAGIC is not set +CONFIG_FB_KYRO=m # CONFIG_FB_3DFX is not set # CONFIG_FB_VOODOO1 is not set # CONFIG_FB_TRIDENT is not set # CONFIG_FB_VIRTUAL is not set -CONFIG_FB_KYRO=m -# CONFIG_FB_PM2 is not set -# CONFIG_FB_PM2_FIFO_DISCONNECT is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_HGA_ACCEL is not set -# CONFIG_FB_3DFX_ACCEL is not set -# CONFIG_FB_TRIDENT_ACCEL is not set -CONFIG_FB_CIRRUS=m # # Console display driver support @@ -1723,7 +1746,8 @@ CONFIG_MDA_CONSOLE=m CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y # CONFIG_FONTS is not set - +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y # # Logo configuration @@ -1742,6 +1766,10 @@ CONFIG_SOUND=m # Advanced Linux Sound Architecture # CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +CONFIG_SND_HWDEP=m +CONFIG_SND_RAWMIDI=m CONFIG_SND_SEQUENCER=m CONFIG_SND_SEQ_DUMMY=m CONFIG_SND_OSSEMUL=y @@ -1751,11 +1779,13 @@ CONFIG_SND_SEQUENCER_OSS=y CONFIG_SND_RTCTIMER=m # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set -CONFIG_SND_BIT32_EMUL=y # # Generic devices # +CONFIG_SND_MPU401_UART=m +CONFIG_SND_OPL3_LIB=m +CONFIG_SND_VX_LIB=m CONFIG_SND_DUMMY=m CONFIG_SND_VIRMIDI=m CONFIG_SND_MTPAV=m @@ -1765,12 +1795,10 @@ CONFIG_SND_MPU401=m # # ISA devices # -# CONFIG_SND_AD1816A is not set # CONFIG_SND_AD1848 is not set # CONFIG_SND_CS4231 is not set # CONFIG_SND_CS4232 is not set # CONFIG_SND_CS4236 is not set -# CONFIG_SND_ES968 is not set # CONFIG_SND_ES1688 is not set # CONFIG_SND_ES18XX is not set # CONFIG_SND_GUSCLASSIC is not set @@ -1784,12 +1812,8 @@ CONFIG_SND_MPU401=m # CONFIG_SND_SB8 is not set # CONFIG_SND_SB16 is not set # CONFIG_SND_SBAWE is not set -# CONFIG_SND_SB16_CSP=y # CONFIG_SND_WAVEFRONT is not set -# CONFIG_SND_ALS100 is not set -# CONFIG_SND_AZT2320 is not set # CONFIG_SND_CMI8330 is not set -# CONFIG_SND_DT019X is not set # CONFIG_SND_OPL3SA2 is not set # CONFIG_SND_SGALAXY is not set # CONFIG_SND_SSCAPE is not set @@ -1797,13 +1821,21 @@ CONFIG_SND_MPU401=m # # PCI devices # +CONFIG_SND_AC97_CODEC=m CONFIG_SND_ALI5451=m +CONFIG_SND_ATIIXP=m +CONFIG_SND_ATIIXP_MODEM=m +CONFIG_SND_AU8810=m +CONFIG_SND_AU8820=m +CONFIG_SND_AU8830=m CONFIG_SND_AZT3328=m +CONFIG_SND_BT87X=m CONFIG_SND_CS46XX=m CONFIG_SND_CS46XX_NEW_DSP=y CONFIG_SND_CS4281=m CONFIG_SND_EMU10K1=m CONFIG_SND_KORG1212=m +CONFIG_SND_MIXART=m CONFIG_SND_NM256=m CONFIG_SND_RME32=m CONFIG_SND_RME96=m @@ -1819,23 +1851,14 @@ CONFIG_SND_ES1938=m CONFIG_SND_ES1968=m CONFIG_SND_MAESTRO3=m CONFIG_SND_FM801=m +CONFIG_SND_FM801_TEA575X=m CONFIG_SND_ICE1712=m CONFIG_SND_ICE1724=m CONFIG_SND_INTEL8X0=m +CONFIG_SND_INTEL8X0M=m CONFIG_SND_SONICVIBES=m CONFIG_SND_VIA82XX=m CONFIG_SND_VX222=m -CONFIG_SND_BT87X=m -CONFIG_SND_ATIIXP=m -CONFIG_SND_ATIIXP_MODEM=m -CONFIG_SND_AU8810=m -CONFIG_SND_AU8820=m -CONFIG_SND_AU8830=m -CONFIG_SND_MIXART=m -CONFIG_SND_FM801_TEA575X=m -CONFIG_SND_INTEL8X0M=m -CONFIG_SND_PDAUDIOCF=m - # # ALSA USB devices @@ -1848,13 +1871,12 @@ CONFIG_SND_USB_USX2Y=m # # CONFIG_SND_VXPOCKET is not set # CONFIG_SND_VXP440 is not set +CONFIG_SND_PDAUDIOCF=m # # Open Sound System # # CONFIG_SOUND_PRIME is not set -# CONFIG_SOUND_BT878 is not set -# CONFIG_SOUND_CMPCI is not set # # USB support @@ -1869,6 +1891,7 @@ CONFIG_USB_DEVICEFS=y # CONFIG_USB_BANDWIDTH is not set # CONFIG_USB_DYNAMIC_MINORS is not set CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG is not set # # USB Host Controller Drivers @@ -1890,9 +1913,9 @@ CONFIG_USB_UHCI_HCD=m CONFIG_USB_MIDI=m CONFIG_USB_ACM=m CONFIG_USB_PRINTER=m -# CONFIG_BLK_DEV_UB is not set CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_RW_DETECT=y CONFIG_USB_STORAGE_DATAFAB=y CONFIG_USB_STORAGE_FREECOM=y CONFIG_USB_STORAGE_ISD200=y @@ -1901,7 +1924,6 @@ CONFIG_USB_STORAGE_HP8200e=y CONFIG_USB_STORAGE_SDDR09=y CONFIG_USB_STORAGE_SDDR55=y CONFIG_USB_STORAGE_JUMPSHOT=y -CONFIG_USB_STORAGE_RW_DETECT=y # # USB Human Interface Devices (HID) @@ -1913,17 +1935,14 @@ CONFIG_HID_PID=y CONFIG_LOGITECH_FF=y CONFIG_THRUSTMASTER_FF=y CONFIG_USB_HIDDEV=y - -# -# USB HID Boot Protocol drivers -# -# CONFIG_USB_KBD is not set -# CONFIG_USB_MOUSE is not set CONFIG_USB_AIPTEK=m CONFIG_USB_WACOM=m CONFIG_USB_KBTAB=m CONFIG_USB_POWERMATE=m +CONFIG_USB_MTOUCH=m +CONFIG_USB_EGALAX=m CONFIG_USB_XPAD=m +CONFIG_USB_ATI_REMOTE=m # # USB Imaging devices @@ -1942,8 +1961,10 @@ CONFIG_USB_IBMCAM=m CONFIG_USB_KONICAWC=m CONFIG_USB_OV511=m CONFIG_USB_SE401=m -CONFIG_USB_STV680=m CONFIG_USB_SN9C102=m +CONFIG_USB_STV680=m +CONFIG_USB_W9968CF=m +CONFIG_USB_PWC=m # # USB Network adaptors @@ -1953,11 +1974,11 @@ CONFIG_USB_KAWETH=m CONFIG_USB_PEGASUS=m CONFIG_USB_RTL8150=m CONFIG_USB_USBNET=m -CONFIG_USB_SPEEDTOUCH=m # # USB Host-to-Host Cables # +CONFIG_USB_ALI_M5632=y CONFIG_USB_AN2720=y CONFIG_USB_BELKIN=y CONFIG_USB_GENESYS=y @@ -2019,40 +2040,33 @@ CONFIG_USB_SERIAL_SAFE_PADDED=y CONFIG_USB_SERIAL_CYBERJACK=m CONFIG_USB_SERIAL_XIRCOM=m CONFIG_USB_SERIAL_OMNINET=m -CONFIG_USB_SERIAL_WHITEHEAT=m CONFIG_USB_EZUSB=y -CONFIG_USB_EMI62=m -CONFIG_USB_LED=m -CONFIG_USB_G_SERIAL=m - # # USB Miscellaneous drivers # +CONFIG_USB_EMI62=m # CONFIG_USB_EMI26 is not set # CONFIG_USB_TIGL is not set CONFIG_USB_AUERSWALD=m CONFIG_USB_RIO500=m -CONFIG_USB_LCD=m -CONFIG_USB_TEST=m -# CONFIG_USB_GADGET is not set -# CONFIG_USB_GADGET_PXA2XX is not set -# CONFIG_USB_GADGET_GOKU is not set -# CONFIG_USB_GADGET_SA1100 is not set -CONFIG_USB_ZERO=m -CONFIG_USB_ETH=m -CONFIG_USB_GADGETFS=m -CONFIG_USB_W9968CF=m -CONFIG_USB_PWC=m CONFIG_USB_LEGOTOWER=m -CONFIG_USB_FILE_STORAGE=m -# CONFIG_USB_FILE_STORAGE_TEST is not set -CONFIG_USB_MTOUCH=m -CONFIG_USB_ATI_REMOTE=m -CONFIG_USB_ALI_M5632=y +CONFIG_USB_LCD=m +CONFIG_USB_LED=m # CONFIG_USB_CYTHERM is not set -CONFIG_USB_EGALAX=m CONFIG_USB_PHIDGETSERVO=m +CONFIG_USB_TEST=m + +# +# USB ATM/DSL drivers +# +CONFIG_USB_ATM=m +CONFIG_USB_SPEEDTOUCH=m + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set # # File systems @@ -2069,20 +2083,9 @@ CONFIG_JBD=m # CONFIG_JBD_DEBUG is not set CONFIG_FS_MBCACHE=y # CONFIG_REISERFS_FS is not set -# CONFIG_REISERFS_CHECK is not set -CONFIG_REISERFS_PROC_INFO=y -CONFIG_REISERFS_FS_XATTR=y -CONFIG_REISERFS_FS_POSIX_ACL=y -CONFIG_REISERFS_FS_SECURITY=y # CONFIG_JFS_FS is not set -# CONFIG_JFS_DEBUG is not set -# CONFIG_JFS_STATISTICS is not set -CONFIG_JFS_POSIX_ACL=y +CONFIG_FS_POSIX_ACL=y # CONFIG_XFS_FS is not set -# CONFIG_XFS_RT is not set -CONFIG_XFS_QUOTA=y -CONFIG_XFS_POSIX_ACL=y -CONFIG_XFS_SECURITY=y # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_QUOTA=y @@ -2100,6 +2103,7 @@ CONFIG_JOLIET=y CONFIG_ZISOFS=y CONFIG_ZISOFS_FS=y CONFIG_UDF_FS=m +CONFIG_UDF_NLS=y # # DOS/FAT/NT Filesystems @@ -2116,6 +2120,7 @@ CONFIG_FAT_DEFAULT_IOCHARSET="ascii" # CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y # CONFIG_DEVFS_FS is not set CONFIG_DEVPTS_FS_XATTR=y CONFIG_DEVPTS_FS_SECURITY=y @@ -2129,27 +2134,27 @@ CONFIG_RAMFS=y # # Miscellaneous filesystems # -# CONFIG_ADFS_FS is not seta +# CONFIG_ADFS_FS is not set # CONFIG_AFFS_FS is not set -# uses sleepon and needs a major update CONFIG_HFS_FS=m CONFIG_HFSPLUS_FS=m # CONFIG_BEFS_FS is not set -# CONFIG_BEFS_DEBUG is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set # CONFIG_JFFS_FS is not set CONFIG_JFFS2_FS=m CONFIG_JFFS2_FS_DEBUG=0 CONFIG_JFFS2_FS_NAND=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=m CONFIG_VXFS_FS=m # CONFIG_HPFS_FS is not set # CONFIG_QNX4FS_FS is not set -# CONFIG_QNX4FS_RW is not set # CONFIG_SYSV_FS is not set # CONFIG_UFS_FS is not set -# CONFIG_UFS_FS_WRITE is not set # # Network File Systems @@ -2176,17 +2181,8 @@ CONFIG_CIFS=m CONFIG_CIFS_XATTR=y CONFIG_CIFS_POSIX=y # CONFIG_NCP_FS is not set -CONFIG_NCPFS_PACKET_SIGNING=y -CONFIG_NCPFS_IOCTL_LOCKING=y -CONFIG_NCPFS_STRONG=y -CONFIG_NCPFS_NFS_NS=y -CONFIG_NCPFS_OS2_NS=y -CONFIG_NCPFS_SMALLDOS=y -CONFIG_NCPFS_NLS=y -CONFIG_NCPFS_EXTRAS=y # CONFIG_CODA_FS is not set # CONFIG_AFS_FS is not set -# CONFIG_RXRPC is not set # # Partition Types @@ -2207,11 +2203,11 @@ CONFIG_SGI_PARTITION=y # CONFIG_ULTRIX_PARTITION is not set CONFIG_SUN_PARTITION=y CONFIG_EFI_PARTITION=y -CONFIG_NLS=y # # Native Language Support # +CONFIG_NLS=y CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m @@ -2236,6 +2232,7 @@ CONFIG_NLS_CODEPAGE_874=m CONFIG_NLS_ISO8859_8=m CONFIG_NLS_CODEPAGE_1250=m CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=y CONFIG_NLS_ISO8859_1=m CONFIG_NLS_ISO8859_2=m CONFIG_NLS_ISO8859_3=m @@ -2250,36 +2247,32 @@ CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m CONFIG_NLS_UTF8=m -CONFIG_NLS_ASCII=y # # Profiling support # -# CONFIG_PROFILING is not set -# CONFIG_OPROFILE is not set - -# -# Tux -# -CONFIG_TUX=m -CONFIG_TUX_EXTCGI=y -# CONFIG_TUX_EXTENDED_LOG is not set -# CONFIG_TUX_DEBUG is not set - +CONFIG_PROFILING=y +CONFIG_OPROFILE=m # # Kernel hacking # CONFIG_DEBUG_KERNEL=y -# CONFIG_DEBUG_SLAB is not set CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y CONFIG_DEBUG_HIGHMEM=y # CONFIG_DEBUG_INFO is not set -CONFIG_DEBUG_SPINLOCK_SLEEP=y # CONFIG_FRAME_POINTER is not set -CONFIG_CC_OPTIMIZE_FOR_SIZE=y -# CONFIG_DEBUG_DRIVER is not set +CONFIG_EARLY_PRINTK=y +CONFIG_DEBUG_STACKOVERFLOW=y +# CONFIG_KPROBES is not set +CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_PAGEALLOC is not set +# CONFIG_SCHEDSTATS is not set +CONFIG_X86_FIND_SMP_CONFIG=y +CONFIG_X86_MPPARSE=y # # Security options @@ -2290,13 +2283,11 @@ CONFIG_SECURITY_CAPABILITIES=y # CONFIG_SECURITY_ROOTPLUG is not set CONFIG_SECURITY_SELINUX=y CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 CONFIG_SECURITY_SELINUX_DISABLE=y CONFIG_SECURITY_SELINUX_DEVELOP=y CONFIG_SECURITY_SELINUX_AVC_STATS=y # CONFIG_SECURITY_SELINUX_MLS is not set -CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 -CONFIG_AUDIT=y -CONFIG_AUDITSYSCALL=y # # Cryptographic options @@ -2309,427 +2300,35 @@ CONFIG_CRYPTO_MD5=m CONFIG_CRYPTO_SHA1=y CONFIG_CRYPTO_SHA256=m CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m CONFIG_CRYPTO_DES=m CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_TWOFISH=m CONFIG_CRYPTO_SERPENT=m -CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_AES_586=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_CAST6=m -CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_TEA=m CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_DEFLATE=m CONFIG_CRYPTO_MICHAEL_MIC=m -# CONFIG_CRYPTO_TEST is not set -CONFIG_LIBCRC32C=m CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_SIGNATURE=y CONFIG_CRYPTO_SIGNATURE_DSA=y CONFIG_CRYPTO_MPILIB=y -CONFIG_CRYPTO_TEA=m -CONFIG_CRYPTO_KHAZAD=m -CONFIG_CRYPTO_WP512=m # # Library routines # -CONFIG_CRC32=m CONFIG_CRC_CCITT=m - +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m CONFIG_ZLIB_INFLATE=y CONFIG_ZLIB_DEFLATE=m +CONFIG_X86_SMP=y +CONFIG_X86_HT=y +CONFIG_X86_BIOS_REBOOT=y +CONFIG_X86_TRAMPOLINE=y CONFIG_PC=y -# CONFIG_SCSI_BUSLOGIC is not set -# CONFIG_SCSI_INIA100 is not set -# CONFIG_ATALK is not set -# CONFIG_DEV_APPLETALK is not set -# CONFIG_LTPC is not set -# CONFIG_COPS is not set -# CONFIG_IPX is not set -# CONFIG_IPDDP is not set -# CONFIG_IRDA is not set -# CONFIG_NCP_FS is not set -# CONFIG_ISAPNP is not set -# CONFIG_PCMCIA_AHA152X is not set -# CONFIG_PCMCIA_NINJA_SCSI is not set -# CONFIG_PCMCIA_QLOGIC is not set -# CONFIG_IEEE1394 is not set -# CONFIG_EL1 is not set -# CONFIG_EL2 is not set -# CONFIG_ELPLUS is not set -# CONFIG_WD80x3 is not set -# CONFIG_IRDA is not set -# CONFIG_GAMEPORT is not set -# CONFIG_DVB is not set -# CONFIG_SND_AD1816A is not set -# CONFIG_SND_AD1848 is not set -# CONFIG_SND_CS4231 is not set -# CONFIG_SND_CS4232 is not set -# CONFIG_SND_CS4236 is not set -# CONFIG_SND_ES968 is not set -# CONFIG_SND_ES1688 is not set -# CONFIG_SND_ES18XX is not set -# CONFIG_SND_GUSCLASSIC is not set -# CONFIG_SND_GUSEXTREME is not set -# CONFIG_SND_GUSMAX is not set -# CONFIG_SND_INTERWAVE is not set -# CONFIG_SND_INTERWAVE_STB is not set -# CONFIG_SND_OPTI92X_AD1848 is not set -# CONFIG_SND_OPTI92X_CS4231 is not set -# CONFIG_SND_OPTI93X is not set -# CONFIG_SND_SB8 is not set -# CONFIG_SND_SB16 is not set -# CONFIG_SND_SBAWE is not set -# CONFIG_SND_SB16_CSP=y -# CONFIG_SND_WAVEFRONT is not set -# CONFIG_SND_ALS100 is not set -# CONFIG_SND_AZT2320 is not set -# CONFIG_SND_CMI8330 is not set -# CONFIG_SND_DT019X is not set -# CONFIG_SND_OPL3SA2 is not set -# CONFIG_SND_SGALAXY is not set -# CONFIG_SND_SSCAPE is not set -# CONFIG_REISERFS_FS is not set -# CONFIG_XFS_FS is not set -# CONFIG_AFFS_FS is not set -# CONFIG_BEFS_FS is not set -# CONFIG_EFS_FS is not set -# CONFIG_BFS_FS is not set -# CONFIG_QNX4FS_FS is not set -# CONFIG_SYSV_FS is not set -# CONFIG_UFS_FS is not set -# CONFIG_WAN_ROUTER is not set -# CONFIG_ROMFS_FS is not set -# CONFIG_MINIX_FS is not set -# CONFIG_BINFMT_AOUT is not set -# CONFIG_DRM_TDFX is not set -# CONFIG_DRM_GAMMA is not set -# CONFIG_DRM_SIS is not set -# CONFIG_BLK_DEV_UMEM is not set -# CONFIG_MWAVE is not set -# CONFIG_ROCKETPORT is not set -# CONFIG_R3964 is not set -# CONFIG_TIPAR is not set -# CONFIG_JOYSTICK_ANALOG is not set -# CONFIG_JOYSTICK_A3D is not set -# CONFIG_JOYSTICK_ADI is not set -# CONFIG_JOYSTICK_COBRA is not set -# CONFIG_JOYSTICK_GF2K is not set -# CONFIG_JOYSTICK_GRIP is not set -# CONFIG_JOYSTICK_GRIP_MP is not set -# CONFIG_JOYSTICK_GUILLEMOT is not set -# CONFIG_JOYSTICK_INTERACT is not set -# CONFIG_JOYSTICK_SIDEWINDER is not set -# CONFIG_JOYSTICK_TMDC is not set -# CONFIG_JOYSTICK_IFORCE is not set -# CONFIG_JOYSTICK_IFORCE_USB=y -# CONFIG_JOYSTICK_IFORCE_232=y -# CONFIG_JOYSTICK_WARRIOR is not set -# CONFIG_JOYSTICK_MAGELLAN is not set -# CONFIG_JOYSTICK_SPACEORB is not set -# CONFIG_JOYSTICK_SPACEBALL is not set -# CONFIG_JOYSTICK_STINGER is not set -# CONFIG_JOYSTICK_TWIDDLER is not set -# CONFIG_JOYSTICK_DB9 is not set -# CONFIG_JOYSTICK_GAMECON is not set -# CONFIG_JOYSTICK_TURBOGRAFX is not set -# CONFIG_RADIO_CADET is not set -# CONFIG_RADIO_RTRACK is not set -# CONFIG_RADIO_RTRACK2 is not set -# CONFIG_RADIO_AZTECH is not set -# CONFIG_RADIO_GEMTEK is not set -# CONFIG_RADIO_GEMTEK_PCI is not set -# CONFIG_RADIO_MAXIRADIO is not set -# CONFIG_RADIO_MAESTRO is not set -# CONFIG_RADIO_MIROPCM20 is not set -# CONFIG_RADIO_MIROPCM20_RDS is not set -# CONFIG_RADIO_SF16FMI is not set -# CONFIG_RADIO_SF16FMR2 is not set -# CONFIG_RADIO_TERRATEC is not set -# CONFIG_RADIO_TRUST is not set -# CONFIG_RADIO_TYPHOON is not set -# CONFIG_RADIO_TYPHOON_PROC_FS=y -# CONFIG_RADIO_ZOLTRIX is not set -# CONFIG_VIDEO_DEV is not set -# CONFIG_PLIP is not set -# CONFIG_FB_MATROX is not set -# CONFIG_FB_3DFX is not set -# CONFIG_FB_HGA is not set -# CONFIG_FB_ATY is not set -# CONFIG_FB_TRIDENT is not set -# CONFIG_FB_VOODOO1 is not set -# CONFIG_SCSI_ADVANSYS is not set -# CONFIG_SCSI_AHA1542 is not set -CONFIG_SCSI_FUTURE_DOMAIN=m -# CONFIG_SCSI_IN2000 is not set -# CONFIG_SCSI_QLOGIC_FAS is not set -# CONFIG_SCSI_QLOGIC_ISP is not set -# CONFIG_VIDEO_BT848 is not set -# CONFIG_VIDEO_PMS is not set -# CONFIG_VIDEO_BWQCAM is not set -# CONFIG_VIDEO_CQCAM is not set -# CONFIG_VIDEO_W9966 is not set -# CONFIG_VIDEO_CPIA is not set -# CONFIG_VIDEO_CPIA_PP is not set -# CONFIG_VIDEO_CPIA_USB is not set -# CONFIG_VIDEO_SAA5249 is not set -# CONFIG_TUNER_3036 is not set -# CONFIG_VIDEO_STRADIS is not set -# CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN is not set -# CONFIG_VIDEO_ZORAN_BUZ is not set -# CONFIG_VIDEO_ZORAN_DC10 is not set -# CONFIG_VIDEO_ZORAN_DC30 is not set -# CONFIG_VIDEO_ZORAN_LML33 is not set -# CONFIG_VIDEO_ZORAN_LML33R10 is not set -# CONFIG_VIDEO_MEYE is not set -# CONFIG_VIDEO_SAA7134 is not set -# CONFIG_VIDEO_MXB is not set -# CONFIG_VIDEO_DPC is not set -# CONFIG_VIDEO_HEXIUM_ORION is not set -# CONFIG_VIDEO_HEXIUM_GEMINI is not set -# CONFIG_VIDEO_CX88 is not set -# CONFIG_VIDEO_SAA5246A is not set -# CONFIG_FB_ATY128 is not set -# CONFIG_FB_RADEON is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_I82092 is not set -# CONFIG_YELLOWFIN is not set -# CONFIG_SUNDANCE is not set -# CONFIG_ULTRA is not set -# CONFIG_SKFP is not set -# CONFIG_DE600 is not set -# CONFIG_DE620 is not set -# CONFIG_CS89x0 is not set -# CONFIG_DGRS is not set -# CONFIG_AC3200 is not set -# CONFIG_NI52 is not set -# CONFIG_NI65 is not set -# CONFIG_LANCE is not set -# CONFIG_ELPLUS is not set -# CONFIG_EL16 is not set -# CONFIG_EL3 is not set -# CONFIG_3C515 is not set -# CONFIG_HAMACHI is not set -CONFIG_HP100=m -# CONFIG_EQUALIZER is not set -# CONFIG_NET_SB1000 is not set -# CONFIG_DEPCA is not set -# CONFIG_ATP is not set -# CONFIG_FB_NEOMAGIC is not set -# CONFIG_INFTL is not set -# CONFIG_MTD_DOC2000 is not set -# CONFIG_MTD_DOC2001PLUS is not set -# CONFIG_MTD_PMC551 is not set -# CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PCI is not set -# CONFIG_PCMCIA_SYM53C500 is not set -# CONFIG_FB_ASILIANT is not set -# CONFIG_FB_HGA_ACCEL is not set -# CONFIG_FB_3DFX_ACCEL is not set -# CONFIG_FB_TRIDENT_ACCEL is not set -# CONFIG_SCSI_DC390T is not set -CONFIG_AUDIT=y -# CONFIG_AUTOFS_FS is not set -# CONFIG_JFS_FS is not set -# CONFIG_I2C_PCA_ISA is not set -CONFIG_RAW_DRIVER=y -# CONFIG_MTD_SCB2_FLASH is not set -CONFIG_UID16=y -CONFIG_X86_PC=y -# CONFIG_X86_ELAN is not set -# CONFIG_X86_VOYAGER is not set -# CONFIG_X86_NUMAQ is not set -# CONFIG_X86_SUMMIT is not set -# CONFIG_X86_BIGSMP is not set -# CONFIG_X86_VISWS is not set -# CONFIG_X86_GENERICARCH is not set -# CONFIG_X86_ES7000 is not set -# CONFIG_M386 is not set -# CONFIG_M486 is not set -# CONFIG_M586 is not set -# CONFIG_M586TSC is not set -# CONFIG_M586MMX is not set -CONFIG_M686=y -# CONFIG_MPENTIUMII is not set -# CONFIG_MPENTIUMIII is not set -# CONFIG_MPENTIUMM is not set -# CONFIG_MPENTIUM4 is not set -# CONFIG_MK6 is not set -# CONFIG_MK7 is not set -# CONFIG_MK8 is not set -# CONFIG_MCRUSOE is not set -# CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set -# CONFIG_MWINCHIP3D is not set -# CONFIG_MCYRIXIII is not set -# CONFIG_MVIAC3_2 is not set -CONFIG_X86_GENERIC=y -CONFIG_X86_CMPXCHG=y -CONFIG_X86_XADD=y -CONFIG_X86_L1_CACHE_SHIFT=7 -CONFIG_RWSEM_XCHGADD_ALGORITHM=y -CONFIG_X86_PPRO_FENCE=y -CONFIG_X86_WP_WORKS_OK=y -CONFIG_X86_INVLPG=y -CONFIG_X86_BSWAP=y -CONFIG_X86_POPAD_OK=y -CONFIG_X86_GOOD_APIC=y -CONFIG_X86_INTEL_USERCOPY=y -CONFIG_X86_USE_PPRO_CHECKSUM=y -# CONFIG_HPET is not set -CONFIG_HPET_TIMER=y -CONFIG_HPET_EMULATE_RTC=y -CONFIG_NR_CPUS=8 -CONFIG_X86_LOCAL_APIC=y -CONFIG_X86_IO_APIC=y -CONFIG_X86_TSC=y -CONFIG_X86_MCE=y -# CONFIG_X86_MCE_NONFATAL is not set -CONFIG_X86_MCE_P4THERMAL=y -CONFIG_TOSHIBA=m -CONFIG_I8K=m -CONFIG_SONYPI=m -CONFIG_MICROCODE=m -CONFIG_X86_MSR=m -CONFIG_X86_CPUID=m -CONFIG_EDD=m -# CONFIG_NUMA is not set -# CONFIG_NOHIGHMEM is not set -CONFIG_HIGHMEM4G=y -# CONFIG_HIGHMEM64G is not set -CONFIG_HIGHMEM=y -CONFIG_HIGHPTE=y -# CONFIG_MATH_EMULATION is not set -CONFIG_MTRR=y -CONFIG_HAVE_DEC_LOCK=y -# CONFIG_X86_UP_APIC is not set -CONFIG_X86_PM_TIMER=y -# CONFIG_X86_4G is not set -# CONFIG_EFI is not set -CONFIG_REGPARM=y -# CONFIG_PCI_GOBIOS is not set -# CONFIG_PCI_GODIRECT is not set -# CONFIG_PCI_GOMMCONFIG is not set -CONFIG_PCI_GOANY=y -CONFIG_MDA_CONSOLE=m -CONFIG_SYNCLINK_CS=m -CONFIG_SYNCLINK=m -CONFIG_SYNCLINKMP=m -CONFIG_HP100=m -CONFIG_PCMCIA_FDOMAIN=m -CONFIG_SCSI_FUTURE_DOMAIN=m -CONFIG_CRASH=m -CONFIG_CAPI_EICON=y -CONFIG_I2O=m -CONFIG_I2O_BLOCK=m -CONFIG_I2O_SCSI=m -CONFIG_I2O_PROC=m -CONFIG_I2O_CONFIG=y -CONFIG_APM=y -# CONFIG_APM_IGNORE_USER_SUSPEND is not set -# CONFIG_APM_DO_ENABLE is not set -CONFIG_APM_CPU_IDLE=y -# CONFIG_APM_DISPLAY_BLANK is not set -CONFIG_APM_RTC_IS_GMT=y -# CONFIG_APM_ALLOW_INTS is not set -# CONFIG_APM_REAL_MODE_POWER_OFF is not set -CONFIG_X86_FIND_SMP_CONFIG=y -CONFIG_X86_MPPARSE=y -CONFIG_ACPI=y -CONFIG_ACPI_BOOT=y -CONFIG_ACPI_INTERPRETER=y -CONFIG_ACPI_SLEEP=y -CONFIG_ACPI_AC=m -CONFIG_ACPI_BATTERY=m -CONFIG_ACPI_BUTTON=m -CONFIG_ACPI_FAN=y -CONFIG_ACPI_PROCESSOR=y -CONFIG_ACPI_THERMAL=y -CONFIG_ACPI_ASUS=m -CONFIG_ACPI_TOSHIBA=m -# CONFIG_ACPI_DEBUG is not set -CONFIG_ACPI_BUS=y -CONFIG_ACPI_EC=y -CONFIG_ACPI_POWER=y -CONFIG_ACPI_PCI=y -CONFIG_ACPI_SYSTEM=y -CONFIG_ACPI_NUMA=y -CONFIG_ACPI_BLACKLIST_YEAR=2001 -CONFIG_X86_ACPI_CPUFREQ=y -# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set -CONFIG_X86_POWERNOW_K6=m -CONFIG_X86_POWERNOW_K7=y -CONFIG_X86_POWERNOW_K8=m -# CONFIG_X86_GX_SUSPMOD is not set -# CONFIG_X86_SPEEDSTEP_CENTRINO is not set -CONFIG_X86_SPEEDSTEP_ICH=y -CONFIG_X86_SPEEDSTEP_SMI=m -CONFIG_X86_SPEEDSTEP_LIB=y -# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set -CONFIG_X86_P4_CLOCKMOD=m -CONFIG_X86_LONGRUN=y -CONFIG_X86_LONGHAUL=y -CONFIG_X86_SMP=y -CONFIG_X86_HT=y -CONFIG_X86_BIOS_REBOOT=y -CONFIG_X86_TRAMPOLINE=y -CONFIG_TUX=m -CONFIG_NVRAM=m -CONFIG_IBM_ASM=m -CONFIG_CRYPTO_AES_586=m -CONFIG_GENERIC_ISA_DMA=y -CONFIG_SCHED_SMT=y -# CONFIG_IRQBALANCE is not set -# CONFIG_SOFTWARE_SUSPEND is not set -# CONFIG_PM_DEBUG is not set -CONFIG_CPU_FREQ=y -# CONFIG_CPU_FREQ_PROC_INTF is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -CONFIG_CPU_FREQ_GOV_PERFORMANCE=y -CONFIG_CPU_FREQ_GOV_POWERSAVE=m -CONFIG_CPU_FREQ_GOV_USERSPACE=m -CONFIG_CPU_FREQ_GOV_ONDEMAND=m -# CONFIG_CPU_FREQ_24_API is not set -CONFIG_CPU_FREQ_TABLE=y -CONFIG_DEBUG_STACKOVERFLOW=y -CONFIG_DEBUG_STACK_USAGE=y -# CONFIG_DEBUG_PAGEALLOC is not set -# CONFIG_KPROBES is not set -CONFIG_KEXEC=y -CONFIG_NETDUMP=m -# CONFIG_SCHEDSTATS is not set -CONFIG_PCI_DIRECT=y -CONFIG_PCI_MMCONFIG=y -CONFIG_PCI_BIOS=y -CONFIG_HOTPLUG_PCI=y -# CONFIG_HOTPLUG_PCI_FAKE is not set -CONFIG_HOTPLUG_PCI_COMPAQ=m -# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set -CONFIG_HOTPLUG_PCI_IBM=m -CONFIG_HOTPLUG_PCI_ACPI=m -CONFIG_HOTPLUG_PCI_ACPI_IBM=m -# CONFIG_HOTPLUG_PCI_CPCI is not set -CONFIG_HOTPLUG_PCI_PCIE=m -# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set -CONFIG_HOTPLUG_PCI_SHPC=m -# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set -# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set -CONFIG_PM=y -CONFIG_IEEE80211=m -# CONFIG_IEEE80211_DEBUG is not set -CONFIG_IEEE80211_CRYPT=m -CONFIG_IEEE80211_WPA=m -CONFIG_IEEE80211_CRYPT_CCMP=m -CONFIG_IEEE80211_CRYPT_TKIP=m -CONFIG_IPW2100=m -# CONFIG_IPW_DEBUG is not set -CONFIG_IPW2100_PROMISC=y -# CONFIG_IPW2100_LEGACY_FW_LOAD is not set -CONFIG_IPW2200=m -CONFIG_M686=y -# CONFIG_NOHIGHMEM is not set -# CONFIG_SMP is not set -CONFIG_HIGHMEM4G=y -# CONFIG_HIGHMEM64G is not set -# CONFIG_PROFILING is not set -# CONFIG_OPROFILE is not set diff --git a/lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config b/lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config new file mode 100644 index 0000000..30aab18 --- /dev/null +++ b/lustre/kernel_patches/kernel_configs/uml-2.6.10-fc3.config @@ -0,0 +1,659 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.10-ac12 +# Tue Jun 14 08:15:05 2005 +# +CONFIG_GENERIC_HARDIRQS=y +CONFIG_USERMODE=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# UML-specific options +# +CONFIG_MODE_TT=y +CONFIG_MODE_SKAS=y +CONFIG_NET=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_MISC=y +CONFIG_HOSTFS=y +CONFIG_MCONSOLE=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_HOST_2G_2G is not set +# CONFIG_SMP is not set +CONFIG_NEST_LEVEL=0 +CONFIG_KERNEL_HALF_GIGS=1 +CONFIG_KERNEL_STACK_ORDER=4 +CONFIG_UML_REAL_TIME_CLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_AUDIT=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_MODULE_SIG is not set +CONFIG_KMOD=y + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Character Devices +# +CONFIG_STDIO_CONSOLE=y +CONFIG_SSL=y +CONFIG_FD_CHAN=y +CONFIG_NULL_CHAN=y +CONFIG_PORT_CHAN=y +CONFIG_PTY_CHAN=y +CONFIG_TTY_CHAN=y +CONFIG_XTERM_CHAN=y +# CONFIG_NOCONFIG_CHAN is not set +CONFIG_CON_ZERO_CHAN="fd:0,fd:1" +CONFIG_CON_CHAN="xterm" +CONFIG_SSL_CHAN="pty" +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set +CONFIG_SOFT_WATCHDOG=m +# CONFIG_UML_WATCHDOG is not set +# CONFIG_UML_SOUND is not set +# CONFIG_SOUND is not set +# CONFIG_HOSTAUDIO is not set + +# +# Block Devices +# +CONFIG_BLK_DEV_UBD=y +# CONFIG_BLK_DEV_UBD_SYNC is not set +CONFIG_BLK_DEV_COW_COMMON=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_NBD=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_NETDEVICES=y + +# +# UML Network Devices +# +CONFIG_UML_NET=y +CONFIG_UML_NET_ETHERTAP=y +CONFIG_UML_NET_TUNTAP=y +# CONFIG_UML_NET_SLIP is not set +# CONFIG_UML_NET_DAEMON is not set +# CONFIG_UML_NET_MCAST is not set +# CONFIG_UML_NET_SLIRP is not set + +# +# Networking support +# + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +CONFIG_NETLINK_DEV=y +CONFIG_UNIX=y +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_FWMARK=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +# CONFIG_IP_PNP is not set +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_TUNNEL=m +CONFIG_IP_TCPDIAG=y +# CONFIG_IP_TCPDIAG_IPV6 is not set + +# +# IP: Virtual Server Configuration +# +CONFIG_IP_VS=m +# CONFIG_IP_VS_DEBUG is not set +CONFIG_IP_VS_TAB_BITS=12 + +# +# IPVS transport protocol load balancing support +# +CONFIG_IP_VS_PROTO_TCP=y +CONFIG_IP_VS_PROTO_UDP=y +CONFIG_IP_VS_PROTO_ESP=y +CONFIG_IP_VS_PROTO_AH=y + +# +# IPVS scheduler +# +CONFIG_IP_VS_RR=m +CONFIG_IP_VS_WRR=m +CONFIG_IP_VS_LC=m +CONFIG_IP_VS_WLC=m +CONFIG_IP_VS_LBLC=m +CONFIG_IP_VS_LBLCR=m +CONFIG_IP_VS_DH=m +CONFIG_IP_VS_SH=m +CONFIG_IP_VS_SED=m +CONFIG_IP_VS_NQ=m + +# +# IPVS application helper +# +CONFIG_IP_VS_FTP=m +CONFIG_IPV6=m +CONFIG_IPV6_PRIVACY=y +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_INET6_TUNNEL=m +CONFIG_IPV6_TUNNEL=m +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_BRIDGE_NETFILTER=y + +# +# IP: Netfilter Configuration +# +CONFIG_IP_NF_CONNTRACK=m +CONFIG_IP_NF_CT_ACCT=y +# CONFIG_IP_NF_CONNTRACK_MARK is not set +CONFIG_IP_NF_CT_PROTO_SCTP=m +CONFIG_IP_NF_FTP=m +CONFIG_IP_NF_IRC=m +CONFIG_IP_NF_TFTP=m +CONFIG_IP_NF_AMANDA=m +CONFIG_IP_NF_QUEUE=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_LIMIT=m +CONFIG_IP_NF_MATCH_IPRANGE=m +CONFIG_IP_NF_MATCH_MAC=m +CONFIG_IP_NF_MATCH_PKTTYPE=m +CONFIG_IP_NF_MATCH_MARK=m +CONFIG_IP_NF_MATCH_MULTIPORT=m +CONFIG_IP_NF_MATCH_TOS=m +CONFIG_IP_NF_MATCH_RECENT=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_DSCP=m +CONFIG_IP_NF_MATCH_AH_ESP=m +CONFIG_IP_NF_MATCH_LENGTH=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_MATCH_TCPMSS=m +CONFIG_IP_NF_MATCH_HELPER=m +CONFIG_IP_NF_MATCH_STATE=m +CONFIG_IP_NF_MATCH_CONNTRACK=m +CONFIG_IP_NF_MATCH_OWNER=m +CONFIG_IP_NF_MATCH_PHYSDEV=m +CONFIG_IP_NF_MATCH_ADDRTYPE=m +CONFIG_IP_NF_MATCH_REALM=m +CONFIG_IP_NF_MATCH_SCTP=m +CONFIG_IP_NF_MATCH_COMMENT=m +# CONFIG_IP_NF_MATCH_HASHLIMIT is not set +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_TARGET_LOG=m +CONFIG_IP_NF_TARGET_ULOG=m +CONFIG_IP_NF_TARGET_TCPMSS=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_NAT_NEEDED=y +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_TARGET_REDIRECT=m +CONFIG_IP_NF_TARGET_NETMAP=m +CONFIG_IP_NF_TARGET_SAME=m +CONFIG_IP_NF_NAT_LOCAL=y +CONFIG_IP_NF_NAT_SNMP_BASIC=m +CONFIG_IP_NF_NAT_IRC=m +CONFIG_IP_NF_NAT_FTP=m +CONFIG_IP_NF_NAT_TFTP=m +CONFIG_IP_NF_NAT_AMANDA=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_TOS=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_DSCP=m +CONFIG_IP_NF_TARGET_MARK=m +CONFIG_IP_NF_TARGET_CLASSIFY=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_TARGET_NOTRACK=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set + +# +# IPv6: Netfilter Configuration +# +# CONFIG_IP6_NF_QUEUE is not set +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_LIMIT=m +CONFIG_IP6_NF_MATCH_MAC=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_MULTIPORT=m +CONFIG_IP6_NF_MATCH_OWNER=m +CONFIG_IP6_NF_MATCH_MARK=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_AHESP=m +CONFIG_IP6_NF_MATCH_LENGTH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_PHYSDEV=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_LOG=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_TARGET_MARK=m +CONFIG_IP6_NF_RAW=m + +# +# Bridge: Netfilter Configuration +# +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_BROUTE=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_BRIDGE_EBT_802_3=m +CONFIG_BRIDGE_EBT_AMONG=m +CONFIG_BRIDGE_EBT_ARP=m +CONFIG_BRIDGE_EBT_IP=m +CONFIG_BRIDGE_EBT_LIMIT=m +CONFIG_BRIDGE_EBT_MARK=m +CONFIG_BRIDGE_EBT_PKTTYPE=m +CONFIG_BRIDGE_EBT_STP=m +CONFIG_BRIDGE_EBT_VLAN=m +CONFIG_BRIDGE_EBT_ARPREPLY=m +CONFIG_BRIDGE_EBT_DNAT=m +CONFIG_BRIDGE_EBT_MARK_T=m +CONFIG_BRIDGE_EBT_REDIRECT=m +CONFIG_BRIDGE_EBT_SNAT=m +CONFIG_BRIDGE_EBT_LOG=m +CONFIG_XFRM=y +CONFIG_XFRM_USER=y + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IP_SCTP=m +# CONFIG_SCTP_DBG_MSG is not set +# CONFIG_SCTP_DBG_OBJCNT is not set +# CONFIG_SCTP_HMAC_NONE is not set +# CONFIG_SCTP_HMAC_SHA1 is not set +CONFIG_SCTP_HMAC_MD5=y +CONFIG_ATM=m +CONFIG_ATM_CLIP=m +# CONFIG_ATM_CLIP_NO_ICMP is not set +CONFIG_ATM_LANE=m +# CONFIG_ATM_MPOA is not set +CONFIG_ATM_BR2684=m +# CONFIG_ATM_BR2684_IPFILTER is not set +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +# CONFIG_DECNET is not set +CONFIG_LLC=m +# CONFIG_LLC2 is not set +CONFIG_IPX=m +# CONFIG_IPX_INTERN is not set +CONFIG_ATALK=m +CONFIG_DEV_APPLETALK=y +CONFIG_IPDDP=m +CONFIG_IPDDP_ENCAP=y +CONFIG_IPDDP_DECAP=y +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +CONFIG_NET_DIVERT=y +# CONFIG_ECONET is not set +CONFIG_WAN_ROUTER=m + +# +# QoS and/or fair queueing +# +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_CLK_JIFFIES=y +# CONFIG_NET_SCH_CLK_GETTIMEOFDAY is not set +# CONFIG_NET_SCH_CLK_CPU is not set +CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m +CONFIG_NET_SCH_HFSC=m +CONFIG_NET_SCH_ATM=m +CONFIG_NET_SCH_PRIO=m +CONFIG_NET_SCH_RED=m +CONFIG_NET_SCH_SFQ=m +CONFIG_NET_SCH_TEQL=m +CONFIG_NET_SCH_TBF=m +CONFIG_NET_SCH_GRED=m +CONFIG_NET_SCH_DSMARK=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_SCH_INGRESS=m +CONFIG_NET_QOS=y +CONFIG_NET_ESTIMATOR=y +CONFIG_NET_CLS=y +CONFIG_NET_CLS_TCINDEX=m +CONFIG_NET_CLS_ROUTE4=m +CONFIG_NET_CLS_ROUTE=y +CONFIG_NET_CLS_FW=m +CONFIG_NET_CLS_U32=m +CONFIG_CLS_U32_PERF=y +CONFIG_NET_CLS_IND=y +CONFIG_NET_CLS_RSVP=m +CONFIG_NET_CLS_RSVP6=m +# CONFIG_NET_CLS_ACT is not set +CONFIG_NET_CLS_POLICE=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_TUX is not set +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=m +CONFIG_ETHERTAP=m + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=m + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ATM drivers +# +# CONFIG_ATM_TCP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +CONFIG_AUTOFS4_FS=m + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_KCORE=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS_XATTR=y +CONFIG_DEVPTS_FS_SECURITY=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_NFS_DIRECTIO=y +CONFIG_NFSD=y +CONFIG_NFSD_V3=y +CONFIG_NFSD_V4=y +CONFIG_NFSD_TCP=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_EXPORTFS=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +CONFIG_RPCSEC_GSS_SPKM3=y +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +CONFIG_OSF_PARTITION=y +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_MAC_PARTITION=y +CONFIG_MSDOS_PARTITION=y +CONFIG_BSD_DISKLABEL=y +CONFIG_MINIX_SUBPARTITION=y +CONFIG_SOLARIS_X86_PARTITION=y +CONFIG_UNIXWARE_DISKLABEL=y +# CONFIG_LDM_PARTITION is not set +CONFIG_SGI_PARTITION=y +# CONFIG_ULTRIX_PARTITION is not set +CONFIG_SUN_PARTITION=y +CONFIG_EFI_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DES=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_SERPENT=m +# CONFIG_CRYPTO_AES is not set +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +# CONFIG_CRYPTO_ANUBIS is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO_SIGNATURE is not set +# CONFIG_CRYPTO_MPILIB is not set + +# +# Library routines +# +CONFIG_CRC_CCITT=m +CONFIG_CRC32=y +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=m +CONFIG_ZLIB_DEFLATE=m + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=y +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID5=m +CONFIG_MD_RAID6=m +CONFIG_MD_MULTIPATH=m +# CONFIG_MD_FAULTY is not set +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +# CONFIG_INPUT is not set + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_SPINLOCK=y +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_INFO=y +CONFIG_FRAME_POINTER=y +CONFIG_PT_PROXY=y +# CONFIG_GPROF is not set +# CONFIG_GCOV is not set diff --git a/lustre/kernel_patches/kernel_configs/uml-vanilla-2.4.24.config b/lustre/kernel_patches/kernel_configs/uml-vanilla-2.4.24.config index 8ffa9c8..7c3217a 100644 --- a/lustre/kernel_patches/kernel_configs/uml-vanilla-2.4.24.config +++ b/lustre/kernel_patches/kernel_configs/uml-vanilla-2.4.24.config @@ -36,7 +36,7 @@ CONFIG_NEST_LEVEL=0 CONFIG_KERNEL_HALF_GIGS=1 # CONFIG_HIGHMEM is not set CONFIG_PROC_MM=y -CONFIG_KERNEL_STACK_ORDER=2 +CONFIG_KERNEL_STACK_ORDER=4 CONFIG_UML_REAL_TIME_CLOCK=y # diff --git a/lustre/kernel_patches/patches/blkdev_tunables-2.4.21-chaos.patch b/lustre/kernel_patches/patches/blkdev_tunables-2.4.21-chaos.patch index 9349422..e863bd6 100644 --- a/lustre/kernel_patches/patches/blkdev_tunables-2.4.21-chaos.patch +++ b/lustre/kernel_patches/patches/blkdev_tunables-2.4.21-chaos.patch @@ -1,6 +1,8 @@ ---- ./drivers/addon/qla2200/qla2x00.h 2004-07-26 12:52:08.000000000 +0100 -+++ ./drivers/addon/qla2200/qla2x00.h 2004-07-26 12:58:42.000000000 +0100 -@@ -3208,7 +3208,7 @@ void qla2x00_setup(char *s); +Index: linux-2.4.21/drivers/addon/qla2200/qla2x00.h +=================================================================== +--- linux-2.4.21.orig/drivers/addon/qla2200/qla2x00.h 2005-06-01 22:51:57.000000000 -0400 ++++ linux-2.4.21/drivers/addon/qla2200/qla2x00.h 2005-06-01 23:06:10.592857440 -0400 +@@ -3275,7 +3275,7 @@ /* Kernel version specific template additions */ /* Number of segments 1 - 65535 */ @@ -9,7 +11,7 @@ /* * Scsi_Host_template (see hosts.h) -@@ -3222,7 +3222,7 @@ void qla2x00_setup(char *s); +@@ -3289,7 +3289,7 @@ * */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,8) @@ -18,23 +20,28 @@ #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 */ diff --git a/lustre/kernel_patches/patches/dev_read_only-2.6-lnxi.patch b/lustre/kernel_patches/patches/dev_read_only-2.6-lnxi.patch index 52d168b..c6b38ab 100644 --- a/lustre/kernel_patches/patches/dev_read_only-2.6-lnxi.patch +++ b/lustre/kernel_patches/patches/dev_read_only-2.6-lnxi.patch @@ -15,11 +15,11 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver /* this is cfs's dev_rdonly check */ if (bio->bi_rw == WRITE && - dev_check_rdonly(bio->bi_bdev->bd_dev)) { -+ dev_check_rdonly(bio->bi_bdev)) { ++ dev_check_rdonly(bio->bi_bdev)) { bio_endio(bio, bio->bi_size, 0); break; } -@@ -3086,53 +3086,85 @@ void swap_io_context(struct io_context * +@@ -3086,53 +3086,86 @@ void swap_io_context(struct io_context * *ioc2 = temp; } @@ -33,8 +33,8 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver */ -void dev_set_rdonly(struct block_device *bdev, int no_write) +struct deventry { -+ dev_t dev; -+ struct deventry *next; ++ dev_t dev; ++ struct deventry *next; +}; + +static struct deventry *devlist = NULL; @@ -46,14 +46,14 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver - printk(KERN_ALERT "%s:%d illegal arg %d (max %d)\n", - __FILE__, __LINE__, no_write, MAX_RDONLY_DEVS); - return; -+ struct deventry *cur; -+ if (!bdev) return 0; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ spin_unlock(&devlock); -+ return 1; ++ struct deventry *cur; ++ if (!bdev) return 0; ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ spin_unlock(&devlock); ++ return 1; } - - if (bdev) { @@ -61,10 +61,10 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver - bdev->bd_disk ? bdev->bd_disk->disk_name : "?", - no_write); - rdonly_devs[no_write] = bdev->bd_dev; -+ cur = cur->next; ++ cur = cur->next; } -+ spin_unlock(&devlock); -+ return 0; ++ spin_unlock(&devlock); ++ return 0; } -void dev_clear_rdonly(int no_write) @@ -75,13 +75,13 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver - __FILE__, __LINE__, no_write, MAX_RDONLY_DEVS); - return; - } -+ struct deventry *newdev, *cur; ++ struct deventry *newdev, *cur; - if (rdonly_devs[no_write] == 0) -+ if (!bdev) -+ return; -+ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); -+ if (!newdev) ++ if (!bdev) ++ return; ++ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); ++ if (!newdev) return; - printk(KERN_WARNING "Clearing read-only at %d\n", no_write); @@ -96,46 +96,47 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver - if (rdonly_devs[i] == dev) - return 1; - return 0; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ spin_unlock(&devlock); -+ kfree(newdev); -+ return; -+} -+ cur = cur->next; -+ } -+ newdev->dev = bdev->bd_dev; -+ newdev->next = devlist; -+ devlist = newdev; -+ spin_unlock(&devlock); -+ printk(KERN_WARNING "Turning device %s read-only\n", -+ bdev->bd_disk ? bdev->bd_disk->disk_name : "?"); ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ spin_unlock(&devlock); ++ kfree(newdev); ++ return; ++ } ++ cur = cur->next; ++ } ++ newdev->dev = bdev->bd_dev; ++ newdev->next = devlist; ++ devlist = newdev; ++ spin_unlock(&devlock); ++ printk(KERN_WARNING "Turning device %s (%#x) read-only\n", ++ bdev->bd_disk ? bdev->bd_disk->disk_name : "", bdev->bd_dev); +} + +void dev_clear_rdonly(struct block_device *bdev) +{ -+ struct deventry *cur, *last = NULL; -+ if (!bdev) return; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ if (last) -+ last->next = cur->next; -+ else -+ devlist = cur->next; -+ spin_unlock(&devlock); -+ kfree(cur); -+ printk(KERN_WARNING "Removing read-only on %s\n", -+ bdev->bd_disk ? bdev->bd_disk->disk_name : "?"); ++ struct deventry *cur, *last = NULL; ++ if (!bdev) return; ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ if (last) ++ last->next = cur->next; ++ else ++ devlist = cur->next; ++ spin_unlock(&devlock); ++ kfree(cur); ++ printk(KERN_WARNING "Removing read-only on %s (%#x)\n", ++ bdev->bd_disk ? bdev->bd_disk->disk_name : ++ "unknown block", bdev->bd_dev); + return; -+ } -+ last = cur; -+ cur = cur->next; -+ } -+ spin_unlock(&devlock); ++ } ++ last = cur; ++ cur = cur->next; ++ } ++ spin_unlock(&devlock); } EXPORT_SYMBOL(dev_set_rdonly); diff --git a/lustre/kernel_patches/patches/dev_read_only-2.6-suse.patch b/lustre/kernel_patches/patches/dev_read_only-2.6-suse.patch index ce244ae..d5a5ac4 100644 --- a/lustre/kernel_patches/patches/dev_read_only-2.6-suse.patch +++ b/lustre/kernel_patches/patches/dev_read_only-2.6-suse.patch @@ -16,7 +16,7 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver + /* this is cfs's dev_rdonly check */ + if (bio->bi_rw == WRITE && -+ dev_check_rdonly(bio->bi_bdev)) { ++ dev_check_rdonly(bio->bi_bdev)) { + bio_endio(bio, bio->bi_size, 0); + break; + } @@ -24,7 +24,7 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver /* * If this device has partitions, remap block n * of partition p to block n+start(p) of the disk. -@@ -3078,6 +3087,91 @@ void swap_io_context(struct io_context * +@@ -3078,6 +3087,92 @@ void swap_io_context(struct io_context * } /* @@ -32,8 +32,8 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver + * silently). This is for filesystem crash/recovery testing. + */ +struct deventry { -+ dev_t dev; -+ struct deventry *next; ++ dev_t dev; ++ struct deventry *next; +}; + +static struct deventry *devlist = NULL; @@ -41,71 +41,72 @@ diff -ur linux-2.6.5-lnxi.orig/drivers/block/ll_rw_blk.c linux-2.6.5-lnxi/driver + +int dev_check_rdonly(struct block_device *bdev) +{ -+ struct deventry *cur; -+ if (!bdev) return 0; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ spin_unlock(&devlock); -+ return 1; ++ struct deventry *cur; ++ if (!bdev) return 0; ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ spin_unlock(&devlock); ++ return 1; + } -+ cur = cur->next; ++ cur = cur->next; + } -+ spin_unlock(&devlock); -+ return 0; ++ spin_unlock(&devlock); ++ return 0; +} + +void dev_set_rdonly(struct block_device *bdev) +{ -+ struct deventry *newdev, *cur; ++ struct deventry *newdev, *cur; + -+ if (!bdev) -+ return; -+ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); -+ if (!newdev) ++ if (!bdev) ++ return; ++ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); ++ if (!newdev) + return; + -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ spin_unlock(&devlock); -+ kfree(newdev); -+ return; -+} -+ cur = cur->next; -+ } -+ newdev->dev = bdev->bd_dev; -+ newdev->next = devlist; -+ devlist = newdev; -+ spin_unlock(&devlock); -+ printk(KERN_WARNING "Turning device %s read-only\n", -+ bdev->bd_disk ? bdev->bd_disk->disk_name : "?"); ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ spin_unlock(&devlock); ++ kfree(newdev); ++ return; ++ } ++ cur = cur->next; ++ } ++ newdev->dev = bdev->bd_dev; ++ newdev->next = devlist; ++ devlist = newdev; ++ spin_unlock(&devlock); ++ printk(KERN_WARNING "Turning device %s (%#x) read-only\n", ++ bdev->bd_disk ? bdev->bd_disk->disk_name : "", bdev->bd_dev); +} + +void dev_clear_rdonly(struct block_device *bdev) +{ -+ struct deventry *cur, *last = NULL; -+ if (!bdev) return; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (bdev->bd_dev == cur->dev) { -+ if (last) -+ last->next = cur->next; -+ else -+ devlist = cur->next; -+ spin_unlock(&devlock); -+ kfree(cur); -+ printk(KERN_WARNING "Removing read-only on %s\n", -+ bdev->bd_disk ? bdev->bd_disk->disk_name : "?"); ++ struct deventry *cur, *last = NULL; ++ if (!bdev) return; ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (bdev->bd_dev == cur->dev) { ++ if (last) ++ last->next = cur->next; ++ else ++ devlist = cur->next; ++ spin_unlock(&devlock); ++ kfree(cur); ++ printk(KERN_WARNING "Removing read-only on %s (%#x)\n", ++ bdev->bd_disk ? bdev->bd_disk->disk_name : ++ "unknown block", bdev->bd_dev); + return; -+ } -+ last = cur; -+ cur = cur->next; -+ } -+ spin_unlock(&devlock); ++ } ++ last = cur; ++ cur = cur->next; ++ } ++ spin_unlock(&devlock); +} + +EXPORT_SYMBOL(dev_set_rdonly); diff --git a/lustre/kernel_patches/patches/dev_read_only_2.4.20-rh.patch b/lustre/kernel_patches/patches/dev_read_only_2.4.20-rh.patch index 44f0ade..c7650fd 100644 --- a/lustre/kernel_patches/patches/dev_read_only_2.4.20-rh.patch +++ b/lustre/kernel_patches/patches/dev_read_only_2.4.20-rh.patch @@ -1,90 +1,88 @@ diff -ur linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c linux-2.4.20-rh/drivers/block/ll_rw_blk.c --- linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c 2004-05-27 11:25:09.000000000 -0700 +++ linux-2.4.20-rh/drivers/block/ll_rw_blk.c 2005-04-08 09:02:14.734804881 -0700 -@@ -645,6 +645,86 @@ void set_device_ro(kdev_t dev,int flag) +@@ -645,6 +645,84 @@ void set_device_ro(kdev_t dev,int flag) else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31)); } -+ +/* -+ * Debug code for turning block devices read-only *silently* (will ++ * Debug code for turning block devices read-only *silently* (will + * discard writes silently). This is only for filesystem crash/recovery + * testing. + */ +struct deventry { -+ kdev_t dev; -+ struct deventry *next; ++ kdev_t dev; ++ struct deventry *next; +}; + +static struct deventry *devlist = NULL; -+static spinlock_t devlock = SPIN_LOCK_UNLOCKED; ++static spinlock_t devlock = SPIN_LOCK_UNLOCKED; + +int dev_check_rdonly(kdev_t dev) { -+ struct deventry *cur; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (dev == cur->dev) { -+ spin_unlock(&devlock); -+ return 1; -+ } -+ cur = cur->next; -+ } -+ spin_unlock(&devlock); -+ return 0; ++ struct deventry *cur; ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (dev == cur->dev) { ++ spin_unlock(&devlock); ++ return 1; ++ } ++ cur = cur->next; ++ } ++ spin_unlock(&devlock); ++ return 0; +} + +void dev_set_rdonly(kdev_t dev) +{ -+ struct deventry *newdev, *cur; -+ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); -+ if (!newdev) return; ++ struct deventry *newdev, *cur; ++ newdev = kmalloc(sizeof(struct deventry), GFP_KERNEL); ++ if (!newdev) return; + -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (dev == cur->dev) { -+ spin_unlock(&devlock); -+ kfree(newdev); -+ return; -+ } -+ cur = cur->next; -+ } -+ newdev->dev = dev; -+ newdev->next = devlist; -+ devlist = newdev; -+ spin_unlock(&devlock); -+ printk(KERN_WARNING "Turning device %s read-only\n", -+ bdevname(dev)); ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (dev == cur->dev) { ++ spin_unlock(&devlock); ++ kfree(newdev); ++ return; ++ } ++ cur = cur->next; ++ } ++ newdev->dev = dev; ++ newdev->next = devlist; ++ devlist = newdev; ++ spin_unlock(&devlock); ++ printk(KERN_WARNING "Turning device %s read-only\n", bdevname(dev)); +} + +void dev_clear_rdonly(kdev_t dev) { -+ struct deventry *cur, *last = NULL; -+ spin_lock(&devlock); -+ cur = devlist; -+ while(cur) { -+ if (dev == cur->dev) { -+ if (last) -+ last->next = cur->next; -+ else -+ devlist = cur->next; -+ spin_unlock(&devlock); -+ kfree(cur); -+ printk(KERN_WARNING "Removing read-only on %s\n", -+ bdevname(dev)); -+ return; -+ } -+ last = cur; -+ cur = cur->next; -+ } -+ spin_unlock(&devlock); ++ struct deventry *cur, *last = NULL; ++ ++ spin_lock(&devlock); ++ cur = devlist; ++ while(cur) { ++ if (dev == cur->dev) { ++ if (last) ++ last->next = cur->next; ++ else ++ devlist = cur->next; ++ spin_unlock(&devlock); ++ kfree(cur); ++ printk(KERN_WARNING "Removing read-only on %s\n", ++ bdevname(dev)); ++ return; ++ } ++ last = cur; ++ cur = cur->next; ++ } ++ spin_unlock(&devlock); +} + +EXPORT_SYMBOL(dev_set_rdonly); +EXPORT_SYMBOL(dev_check_rdonly); +EXPORT_SYMBOL(dev_clear_rdonly); + -+ inline void drive_stat_acct (kdev_t dev, int rw, unsigned long nr_sectors, int new_io) { @@ -92,10 +90,10 @@ diff -ur linux-2.4.20-rh.orig/drivers/block/ll_rw_blk.c linux-2.4.20-rh/drivers/ buffer_IO_error(bh); break; } -+ if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) { -+ bh->b_end_io(bh, 0); -+ break; -+ } ++ if ((rw & WRITE)&&(dev_check_rdonly(bh->b_rdev))) { ++ bh->b_end_io(bh, 0); ++ break; ++ } } while (q->make_request_fn(q, rw, bh)); } diff --git a/lustre/kernel_patches/patches/dev_read_only_2.4.21-chaos.patch b/lustre/kernel_patches/patches/dev_read_only_2.4.21-chaos.patch index e37a15d..3902db5 100644 --- a/lustre/kernel_patches/patches/dev_read_only_2.4.21-chaos.patch +++ b/lustre/kernel_patches/patches/dev_read_only_2.4.21-chaos.patch @@ -1,6 +1,6 @@ --- linux-2.4.24.orig/drivers/block/ll_rw_blk.c 2005-04-07 17:30:58.978035892 -0700 +++ linux-2.4.24/drivers/block/ll_rw_blk.c 2005-04-07 17:22:04.354867801 -0700 -@@ -691,6 +691,86 @@ void set_device_ro(kdev_t dev,int flag) +@@ -691,6 +691,85 @@ void set_device_ro(kdev_t dev,int flag) else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31)); } @@ -11,72 +11,71 @@ + * 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); @@ -91,10 +90,10 @@ 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)); } @@ -117,7 +116,7 @@ bdev->bd_op = NULL; unlock_kernel(); up(&bdev->bd_sem); -+ dev_clear_rdonly(to_kdev_t(bdev->bd_dev)); ++ dev_clear_rdonly(to_kdev_t(bdev->bd_dev)); bdput(bdev); return ret; } diff --git a/lustre/kernel_patches/patches/elevator-cfq.patch b/lustre/kernel_patches/patches/elevator-cfq.patch new file mode 100644 index 0000000..a13194e --- /dev/null +++ b/lustre/kernel_patches/patches/elevator-cfq.patch @@ -0,0 +1,20 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/drivers/block/ll_rw_blk.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/drivers/block/ll_rw_blk.c 2005-06-28 01:53:39.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/drivers/block/ll_rw_blk.c 2005-06-28 01:58:45.000000000 -0600 +@@ -1380,12 +1380,12 @@ + static int __make_request(request_queue_t *, struct bio *); + + static elevator_t *chosen_elevator = +-#if defined(CONFIG_IOSCHED_AS) ++#if defined(CONFIG_IOSCHED_CFQ) ++ &iosched_cfq; ++#elif defined(CONFIG_IOSCHED_AS) + &iosched_as; + #elif defined(CONFIG_IOSCHED_DEADLINE) + &iosched_deadline; +-#elif defined(CONFIG_IOSCHED_CFQ) +- &iosched_cfq; + #elif defined(CONFIG_IOSCHED_NOOP) + &elevator_noop; + #else diff --git a/lustre/kernel_patches/patches/export-show_task-2.4-cray.patch b/lustre/kernel_patches/patches/export-show_task-2.4-cray.patch new file mode 100644 index 0000000..8211401 --- /dev/null +++ b/lustre/kernel_patches/patches/export-show_task-2.4-cray.patch @@ -0,0 +1,33 @@ +Index: kernel-l0405/kernel/sched.c +=================================================================== +--- kernel-l0405.orig/kernel/sched.c 2003-11-06 16:15:20.000000000 -0800 ++++ kernel-l0405/kernel/sched.c 2005-04-05 14:44:27.000000000 -0700 +@@ -1627,7 +1627,7 @@ + return retval; + } + +-static void show_task(task_t * p) ++void show_task(task_t * p) + { + unsigned long free = 0; + int state; +Index: kernel-l0405/kernel/ksyms.c +=================================================================== +--- kernel-l0405.orig/kernel/ksyms.c 2005-04-05 14:44:15.000000000 -0700 ++++ kernel-l0405/kernel/ksyms.c 2005-04-05 14:44:50.000000000 -0700 +@@ -55,6 +55,7 @@ + #include + #include + ++extern void show_task(task_t *); + + #if defined(CONFIG_PROC_FS) + #include +@@ -684,6 +685,7 @@ + + /* debug */ + EXPORT_SYMBOL(dump_stack); ++EXPORT_SYMBOL(show_task); + + #if defined(CONFIG_KDB_USB) + #include diff --git a/lustre/kernel_patches/patches/export-show_task-2.6-vanilla.patch b/lustre/kernel_patches/patches/export-show_task-2.6-vanilla.patch index 81f62ff..828d3ab 100644 --- a/lustre/kernel_patches/patches/export-show_task-2.6-vanilla.patch +++ b/lustre/kernel_patches/patches/export-show_task-2.6-vanilla.patch @@ -11,8 +11,12 @@ Index: linux-2.6.5-SLES9_SP1_BRANCH_2004102113353091/kernel/sched.c { task_t *relative; unsigned state; -@@ -3200,6 +3200,7 @@ - if (state != TASK_RUNNING) +@@ -3200,9 +3200,10 @@ + else + printk(" (NOTLB)\n"); + +- if (state != TASK_RUNNING) ++ if (state != TASK_RUNNING || p == current) show_stack(p, NULL); } +EXPORT_SYMBOL(show_task); diff --git a/lustre/kernel_patches/patches/ext-2.4-patch-1-2.4.19-suse.patch b/lustre/kernel_patches/patches/ext-2.4-patch-1-2.4.19-suse.patch index a6b126e..1cdaa93 100644 --- a/lustre/kernel_patches/patches/ext-2.4-patch-1-2.4.19-suse.patch +++ b/lustre/kernel_patches/patches/ext-2.4-patch-1-2.4.19-suse.patch @@ -1079,7 +1079,7 @@ Index: linux-2.4.19.SuSE/fs/ext3/namei.c ret = bh; goto cleanup_and_exit; } else { -@@ -198,6 +851,66 @@ +@@ -198,6 +851,74 @@ return ret; } @@ -1098,17 +1098,25 @@ Index: linux-2.4.19.SuSE/fs/ext3/namei.c + int namelen = dentry->d_name.len; + const u8 *name = dentry->d_name.name; + struct inode *dir = dentry->d_parent->d_inode; -+ ++ + sb = dir->i_sb; -+ if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) -+ return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); + if (!(bh = ext3_bread (NULL,dir, block, 0, err))) + goto errout; + de = (struct ext3_dir_entry_2 *) bh->b_data; -+ top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize - ++ top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize - + EXT3_DIR_REC_LEN(0)); + for (; de < top; de = ext3_next_entry(de)) + if (ext3_match (namelen, name, de)) { diff --git a/lustre/kernel_patches/patches/ext-2.4-patch-1.patch b/lustre/kernel_patches/patches/ext-2.4-patch-1.patch index 28a1ad6..c5e1ee0 100644 --- a/lustre/kernel_patches/patches/ext-2.4-patch-1.patch +++ b/lustre/kernel_patches/patches/ext-2.4-patch-1.patch @@ -1303,7 +1303,7 @@ ret = bh; goto cleanup_and_exit; } else { -@@ -196,6 +849,66 @@ cleanup_and_exit: +@@ -196,6 +849,74 @@ cleanup_and_exit: return ret; } @@ -1322,17 +1322,25 @@ + int namelen = dentry->d_name.len; + const u8 *name = dentry->d_name.name; + struct inode *dir = dentry->d_parent->d_inode; -+ ++ + sb = dir->i_sb; -+ if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) -+ return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); + if (!(bh = ext3_bread (NULL,dir, block, 0, err))) + goto errout; + de = (struct ext3_dir_entry_2 *) bh->b_data; -+ top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize - ++ top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize - + EXT3_DIR_REC_LEN(0)); + for (; de < top; de = ext3_next_entry(de)) + if (ext3_match (namelen, name, de)) { diff --git a/lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch b/lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch new file mode 100644 index 0000000..39c47a7 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-delete_thread-2.4.29.patch @@ -0,0 +1,442 @@ +Index: linux-2.4.29/fs/ext3/super.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/super.c 2005-05-03 15:53:33.047533872 +0300 ++++ linux-2.4.29/fs/ext3/super.c 2005-05-03 15:54:47.192262160 +0300 +@@ -400,6 +400,127 @@ + } + } + ++#ifdef EXT3_DELETE_THREAD ++/* ++ * Delete inodes in a loop until there are no more to be deleted. ++ * Normally, we run in the background doing the deletes and sleeping again, ++ * and clients just add new inodes to be deleted onto the end of the list. ++ * If someone is concerned about free space (e.g. block allocation or similar) ++ * then they can sleep on s_delete_waiter_queue and be woken up when space ++ * has been freed. ++ */ ++int ext3_delete_thread(void *data) ++{ ++ struct super_block *sb = data; ++ struct ext3_sb_info *sbi = EXT3_SB(sb); ++ struct task_struct *tsk = current; ++ ++ /* Almost like daemonize, but not quite */ ++ exit_mm(current); ++ tsk->session = 1; ++ tsk->pgrp = 1; ++ tsk->tty = NULL; ++ exit_files(current); ++ reparent_to_init(); ++ ++ sprintf(tsk->comm, "kdelext3-%s", kdevname(sb->s_dev)); ++ sigfillset(&tsk->blocked); ++ ++ /*tsk->flags |= PF_KERNTHREAD;*/ ++ ++ INIT_LIST_HEAD(&sbi->s_delete_list); ++ wake_up(&sbi->s_delete_waiter_queue); ++ ext3_debug("delete thread on %s started\n", kdevname(sb->s_dev)); ++ ++ /* main loop */ ++ for (;;) { ++ wait_event_interruptible(sbi->s_delete_thread_queue, ++ !list_empty(&sbi->s_delete_list) || ++ !test_opt(sb, ASYNCDEL)); ++ ext3_debug("%s woken up: %lu inodes, %lu blocks\n", ++ tsk->comm,sbi->s_delete_inodes,sbi->s_delete_blocks); ++ ++ spin_lock(&sbi->s_delete_lock); ++ if (list_empty(&sbi->s_delete_list)) { ++ clear_opt(sbi->s_mount_opt, ASYNCDEL); ++ memset(&sbi->s_delete_list, 0, ++ sizeof(sbi->s_delete_list)); ++ spin_unlock(&sbi->s_delete_lock); ++ ext3_debug("delete thread on %s exiting\n", ++ kdevname(sb->s_dev)); ++ wake_up(&sbi->s_delete_waiter_queue); ++ break; ++ } ++ ++ while (!list_empty(&sbi->s_delete_list)) { ++ struct inode *inode=list_entry(sbi->s_delete_list.next, ++ struct inode, i_devices); ++ unsigned long blocks = inode->i_blocks >> ++ (inode->i_blkbits - 9); ++ ++ list_del_init(&inode->i_devices); ++ spin_unlock(&sbi->s_delete_lock); ++ ext3_debug("%s delete ino %lu blk %lu\n", ++ tsk->comm, inode->i_ino, blocks); ++ ++ J_ASSERT(EXT3_I(inode)->i_state & EXT3_STATE_DELETE); ++ J_ASSERT(inode->i_nlink == 1); ++ inode->i_nlink = 0; ++ iput(inode); ++ ++ spin_lock(&sbi->s_delete_lock); ++ sbi->s_delete_blocks -= blocks; ++ sbi->s_delete_inodes--; ++ } ++ if (sbi->s_delete_blocks != 0 || sbi->s_delete_inodes != 0) { ++ ext3_warning(sb, __FUNCTION__, ++ "%lu blocks, %lu inodes on list?\n", ++ sbi->s_delete_blocks,sbi->s_delete_inodes); ++ sbi->s_delete_blocks = 0; ++ sbi->s_delete_inodes = 0; ++ } ++ spin_unlock(&sbi->s_delete_lock); ++ wake_up(&sbi->s_delete_waiter_queue); ++ } ++ ++ return 0; ++} ++ ++static void ext3_start_delete_thread(struct super_block *sb) ++{ ++ struct ext3_sb_info *sbi = EXT3_SB(sb); ++ int rc; ++ ++ spin_lock_init(&sbi->s_delete_lock); ++ init_waitqueue_head(&sbi->s_delete_thread_queue); ++ init_waitqueue_head(&sbi->s_delete_waiter_queue); ++ ++ if (!test_opt(sb, ASYNCDEL)) ++ return; ++ ++ rc = kernel_thread(ext3_delete_thread, sb, CLONE_VM | CLONE_FILES); ++ if (rc < 0) ++ printk(KERN_ERR "EXT3-fs: cannot start delete thread: rc %d\n", ++ rc); ++ else ++ wait_event(sbi->s_delete_waiter_queue, sbi->s_delete_list.next); ++} ++ ++static void ext3_stop_delete_thread(struct ext3_sb_info *sbi) ++{ ++ if (sbi->s_delete_list.next == 0) /* thread never started */ ++ return; ++ ++ clear_opt(sbi->s_mount_opt, ASYNCDEL); ++ wake_up(&sbi->s_delete_thread_queue); ++ wait_event(sbi->s_delete_waiter_queue, ++ sbi->s_delete_list.next == 0 && sbi->s_delete_inodes == 0); ++} ++#else ++#define ext3_start_delete_thread(sbi) do {} while(0) ++#define ext3_stop_delete_thread(sbi) do {} while(0) ++#endif /* EXT3_DELETE_THREAD */ ++ + void ext3_put_super (struct super_block * sb) + { + struct ext3_sb_info *sbi = EXT3_SB(sb); +@@ -407,6 +528,9 @@ + kdev_t j_dev = sbi->s_journal->j_dev; + int i; + ++#ifdef EXT3_DELETE_THREAD ++ J_ASSERT(sbi->s_delete_inodes == 0); ++#endif + ext3_xattr_put_super(sb); + journal_destroy(sbi->s_journal); + if (!(sb->s_flags & MS_RDONLY)) { +@@ -526,6 +650,13 @@ + clear_opt (*mount_options, XATTR_USER); + else + #endif ++#ifdef EXT3_DELETE_THREAD ++ if (!strcmp(this_char, "asyncdel")) ++ set_opt(*mount_options, ASYNCDEL); ++ else if (!strcmp(this_char, "noasyncdel")) ++ clear_opt(*mount_options, ASYNCDEL); ++ else ++#endif + if (!strcmp (this_char, "bsddf")) + clear_opt (*mount_options, MINIX_DF); + else if (!strcmp (this_char, "nouid32")) { +@@ -1244,6 +1375,7 @@ + } + + ext3_setup_super (sb, es, sb->s_flags & MS_RDONLY); ++ ext3_start_delete_thread(sb); + EXT3_SB(sb)->s_mount_state |= EXT3_ORPHAN_FS; + ext3_orphan_cleanup(sb, es); + EXT3_SB(sb)->s_mount_state &= ~EXT3_ORPHAN_FS; +@@ -1626,7 +1758,12 @@ + static int ext3_sync_fs(struct super_block *sb) + { + tid_t target; +- ++ ++ if (atomic_read(&sb->s_active) == 0) { ++ /* fs is being umounted: time to stop delete thread */ ++ ext3_stop_delete_thread(EXT3_SB(sb)); ++ } ++ + sb->s_dirt = 0; + target = log_start_commit(EXT3_SB(sb)->s_journal, NULL); + log_wait_commit(EXT3_SB(sb)->s_journal, target); +@@ -1690,6 +1827,9 @@ + if (!parse_options(data, &tmp, sbi, &tmp, 1)) + return -EINVAL; + ++ if (!test_opt(sb, ASYNCDEL) || (*flags & MS_RDONLY)) ++ ext3_stop_delete_thread(sbi); ++ + if (sbi->s_mount_opt & EXT3_MOUNT_ABORT) + ext3_abort(sb, __FUNCTION__, "Abort forced by user"); + +Index: linux-2.4.29/fs/ext3/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/inode.c 2005-05-03 15:53:36.555000656 +0300 ++++ linux-2.4.29/fs/ext3/inode.c 2005-05-03 15:53:56.901907456 +0300 +@@ -2562,6 +2562,118 @@ + return err; + } + ++#ifdef EXT3_DELETE_THREAD ++/* Move blocks from to-be-truncated inode over to a new inode, and delete ++ * that one from the delete thread instead. This avoids a lot of latency ++ * when truncating large files. ++ * ++ * If we have any problem deferring the truncate, just truncate it right away. ++ * If we defer it, we also mark how many blocks it would free, so that we ++ * can keep the statfs data correct, and we know if we should sleep on the ++ * delete thread when we run out of space. ++ */ ++void ext3_truncate_thread(struct inode *old_inode) ++{ ++ struct ext3_sb_info *sbi = EXT3_SB(old_inode->i_sb); ++ struct ext3_inode_info *nei, *oei = EXT3_I(old_inode); ++ struct inode *new_inode; ++ handle_t *handle; ++ unsigned long blocks = old_inode->i_blocks >> (old_inode->i_blkbits-9); ++ ++ if (!test_opt(old_inode->i_sb, ASYNCDEL) || !sbi->s_delete_list.next) ++ goto out_truncate; ++ ++ /* XXX This is a temporary limitation for code simplicity. ++ * We could truncate to arbitrary sizes at some later time. ++ */ ++ if (old_inode->i_size != 0) ++ goto out_truncate; ++ ++ /* We may want to truncate the inode immediately and not defer it */ ++ if (IS_SYNC(old_inode) || blocks <= EXT3_NDIR_BLOCKS || ++ old_inode->i_size > oei->i_disksize) ++ goto out_truncate; ++ ++ /* We can't use the delete thread as-is during real orphan recovery, ++ * as we add to the orphan list here, causing ext3_orphan_cleanup() ++ * to loop endlessly. It would be nice to do so, but needs work. ++ */ ++ if (oei->i_state & EXT3_STATE_DELETE || ++ sbi->s_mount_state & EXT3_ORPHAN_FS) { ++ ext3_debug("doing deferred inode %lu delete (%lu blocks)\n", ++ old_inode->i_ino, blocks); ++ goto out_truncate; ++ } ++ ++ ext3_discard_prealloc(old_inode); ++ ++ /* old_inode = 1 ++ * new_inode = sb + GDT + ibitmap ++ * orphan list = 1 inode/superblock for add, 2 inodes for del ++ * quota files = 2 * EXT3_SINGLEDATA_TRANS_BLOCKS ++ */ ++ handle = ext3_journal_start(old_inode, 7); ++ if (IS_ERR(handle)) ++ goto out_truncate; ++ ++ new_inode = ext3_new_inode(handle, old_inode, old_inode->i_mode); ++ if (IS_ERR(new_inode)) { ++ ext3_debug("truncate inode %lu directly (no new inodes)\n", ++ old_inode->i_ino); ++ goto out_journal; ++ } ++ ++ nei = EXT3_I(new_inode); ++ ++ down_write(&oei->truncate_sem); ++ new_inode->i_size = old_inode->i_size; ++ new_inode->i_blocks = old_inode->i_blocks; ++ new_inode->i_uid = old_inode->i_uid; ++ new_inode->i_gid = old_inode->i_gid; ++ new_inode->i_nlink = 1; ++ ++ /* FIXME when we do arbitrary truncates */ ++ old_inode->i_blocks = oei->i_file_acl ? old_inode->i_blksize / 512 : 0; ++ old_inode->i_mtime = old_inode->i_ctime = CURRENT_TIME; ++ ++ memcpy(nei->i_data, oei->i_data, sizeof(nei->i_data)); ++ memset(oei->i_data, 0, sizeof(oei->i_data)); ++ ++ nei->i_disksize = oei->i_disksize; ++ nei->i_state |= EXT3_STATE_DELETE; ++ up_write(&oei->truncate_sem); ++ ++ if (ext3_orphan_add(handle, new_inode) < 0) ++ goto out_journal; ++ ++ if (ext3_orphan_del(handle, old_inode) < 0) { ++ ext3_orphan_del(handle, new_inode); ++ iput(new_inode); ++ goto out_journal; ++ } ++ ++ ext3_journal_stop(handle, old_inode); ++ ++ spin_lock(&sbi->s_delete_lock); ++ J_ASSERT(list_empty(&new_inode->i_devices)); ++ list_add_tail(&new_inode->i_devices, &sbi->s_delete_list); ++ sbi->s_delete_blocks += blocks; ++ sbi->s_delete_inodes++; ++ spin_unlock(&sbi->s_delete_lock); ++ ++ ext3_debug("delete inode %lu (%lu blocks) by thread\n", ++ new_inode->i_ino, blocks); ++ ++ wake_up(&sbi->s_delete_thread_queue); ++ return; ++ ++out_journal: ++ ext3_journal_stop(handle, old_inode); ++out_truncate: ++ ext3_truncate(old_inode); ++} ++#endif /* EXT3_DELETE_THREAD */ ++ + /* + * On success, We end up with an outstanding reference count against + * iloc->bh. This _must_ be cleaned up later. +Index: linux-2.4.29/fs/ext3/file.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/file.c 2005-04-07 19:31:00.000000000 +0300 ++++ linux-2.4.29/fs/ext3/file.c 2005-05-03 15:53:56.902907304 +0300 +@@ -123,7 +123,11 @@ + }; + + struct inode_operations ext3_file_inode_operations = { ++#ifdef EXT3_DELETE_THREAD ++ truncate: ext3_truncate_thread, /* BKL held */ ++#else + truncate: ext3_truncate, /* BKL held */ ++#endif + setattr: ext3_setattr, /* BKL held */ + setxattr: ext3_setxattr, /* BKL held */ + getxattr: ext3_getxattr, /* BKL held */ +Index: linux-2.4.29/fs/ext3/namei.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/namei.c 2005-05-03 15:53:33.044534328 +0300 ++++ linux-2.4.29/fs/ext3/namei.c 2005-05-03 15:53:56.905906848 +0300 +@@ -838,6 +838,40 @@ + return retval; + } + ++#ifdef EXT3_DELETE_THREAD ++static int ext3_try_to_delay_deletion(struct inode *inode) ++{ ++ struct ext3_sb_info *sbi = EXT3_SB(inode->i_sb); ++ struct ext3_inode_info *ei = EXT3_I(inode); ++ unsigned long blocks; ++ ++ if (!test_opt(inode->i_sb, ASYNCDEL)) ++ return 0; ++ ++ /* We may want to delete the inode immediately and not defer it */ ++ blocks = inode->i_blocks >> (inode->i_blkbits - 9); ++ if (IS_SYNC(inode) || blocks <= EXT3_NDIR_BLOCKS) ++ return 0; ++ ++ inode->i_nlink = 1; ++ atomic_inc(&inode->i_count); ++ ei->i_state |= EXT3_STATE_DELETE; ++ ++ spin_lock(&sbi->s_delete_lock); ++ J_ASSERT(list_empty(&inode->i_devices)); ++ list_add_tail(&inode->i_devices, &sbi->s_delete_list); ++ sbi->s_delete_blocks += blocks; ++ sbi->s_delete_inodes++; ++ spin_unlock(&sbi->s_delete_lock); ++ ++ wake_up(&sbi->s_delete_thread_queue); ++ ++ return 0; ++} ++#else ++#define ext3_try_to_delay_deletion(inode) do {} while (0) ++#endif ++ + static int ext3_unlink(struct inode * dir, struct dentry *dentry) + { + int retval; +@@ -878,8 +912,10 @@ + dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; + ext3_mark_inode_dirty(handle, dir); + inode->i_nlink--; +- if (!inode->i_nlink) ++ if (!inode->i_nlink) { ++ ext3_try_to_delay_deletion(inode); + ext3_orphan_add(handle, inode); ++ } + inode->i_ctime = dir->i_ctime; + ext3_mark_inode_dirty(handle, inode); + retval = 0; +Index: linux-2.4.29/include/linux/ext3_fs.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs.h 2005-05-03 15:53:37.124914016 +0300 ++++ linux-2.4.29/include/linux/ext3_fs.h 2005-05-03 15:53:56.907906544 +0300 +@@ -188,6 +188,7 @@ + */ + #define EXT3_STATE_JDATA 0x00000001 /* journaled data exists */ + #define EXT3_STATE_NEW 0x00000002 /* inode is newly created */ ++#define EXT3_STATE_DELETE 0x00000010 /* deferred delete inode */ + + /* + * ioctl commands +@@ -315,6 +316,7 @@ + #define EXT3_MOUNT_UPDATE_JOURNAL 0x1000 /* Update the journal format */ + #define EXT3_MOUNT_NO_UID32 0x2000 /* Disable 32-bit UIDs */ + #define EXT3_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */ ++#define EXT3_MOUNT_ASYNCDEL 0x20000 /* Delayed deletion */ + + /* Compatibility, for having both ext2_fs.h and ext3_fs.h included at once */ + #ifndef _LINUX_EXT2_FS_H +@@ -639,6 +641,9 @@ + extern void ext3_dirty_inode(struct inode *); + extern int ext3_change_inode_journal_flag(struct inode *, int); + extern void ext3_truncate (struct inode *); ++#ifdef EXT3_DELETE_THREAD ++extern void ext3_truncate_thread(struct inode *inode); ++#endif + extern void ext3_set_inode_flags(struct inode *); + + /* ioctl.c */ +Index: linux-2.4.29/include/linux/ext3_fs_sb.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs_sb.h 2005-05-03 15:53:33.048533720 +0300 ++++ linux-2.4.29/include/linux/ext3_fs_sb.h 2005-05-03 15:53:56.909906240 +0300 +@@ -29,6 +29,8 @@ + + #define EXT3_MAX_GROUP_LOADED 8 + ++#define EXT3_DELETE_THREAD ++ + /* + * third extended-fs super-block data in memory + */ +@@ -74,6 +76,14 @@ + struct timer_list turn_ro_timer; /* For turning read-only (crash simulation) */ + wait_queue_head_t ro_wait_queue; /* For people waiting for the fs to go read-only */ + #endif ++#ifdef EXT3_DELETE_THREAD ++ spinlock_t s_delete_lock; ++ struct list_head s_delete_list; ++ unsigned long s_delete_blocks; ++ unsigned long s_delete_inodes; ++ wait_queue_head_t s_delete_thread_queue; ++ wait_queue_head_t s_delete_waiter_queue; ++#endif + }; + + #endif /* _LINUX_EXT3_FS_SB */ diff --git a/lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch b/lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch new file mode 100644 index 0000000..f4832af --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-ea-in-inode-2.4.29.patch @@ -0,0 +1,733 @@ +Index: linux-2.4.29/fs/ext3/ialloc.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/ialloc.c 2005-05-03 15:56:43.831530296 +0300 ++++ linux-2.4.29/fs/ext3/ialloc.c 2005-05-03 16:07:32.990843080 +0300 +@@ -576,6 +576,12 @@ + insert_inode_hash(inode); + inode->i_generation = sb->u.ext3_sb.s_next_generation++; + ++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) { ++ inode->u.ext3_i.i_extra_isize = sizeof(__u16) /* i_extra_isize */ ++ + sizeof(__u16); /* i_pad1 */ ++ } else ++ inode->u.ext3_i.i_extra_isize = 0; ++ + inode->u.ext3_i.i_state = EXT3_STATE_NEW; + err = ext3_get_inode_loc_new(inode, &iloc, 1); + if (err) goto fail; +Index: linux-2.4.29/fs/ext3/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/inode.c 2005-05-03 15:58:30.758274960 +0300 ++++ linux-2.4.29/fs/ext3/inode.c 2005-05-03 16:07:32.995842320 +0300 +@@ -2240,6 +2240,12 @@ + inode->u.ext3_i.i_data[block] = iloc.raw_inode->i_block[block]; + INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); + ++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) ++ inode->u.ext3_i.i_extra_isize = ++ le16_to_cpu(raw_inode->i_extra_isize); ++ else ++ inode->u.ext3_i.i_extra_isize = 0; ++ + if (S_ISREG(inode->i_mode)) { + inode->i_op = &ext3_file_inode_operations; + inode->i_fop = &ext3_file_operations; +@@ -2367,6 +2373,10 @@ + else for (block = 0; block < EXT3_N_BLOCKS; block++) + raw_inode->i_block[block] = inode->u.ext3_i.i_data[block]; + ++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) ++ raw_inode->i_extra_isize = ++ cpu_to_le16(EXT3_I(inode)->i_extra_isize); ++ + BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); + rc = ext3_journal_dirty_metadata(handle, bh); + if (!err) +Index: linux-2.4.29/fs/ext3/xattr.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/xattr.c 2005-04-07 19:31:00.000000000 +0300 ++++ linux-2.4.29/fs/ext3/xattr.c 2005-05-03 16:07:33.007840496 +0300 +@@ -100,6 +100,9 @@ + static int ext3_xattr_set2(handle_t *, struct inode *, struct buffer_head *, + struct ext3_xattr_header *); + ++int ext3_xattr_block_set(handle_t *, struct inode *, int, const char *, ++ const void *, size_t, int); ++ + #ifdef CONFIG_EXT3_FS_XATTR_SHARING + + static int ext3_xattr_cache_insert(struct buffer_head *); +@@ -348,17 +351,12 @@ + } + + /* +- * ext3_xattr_get() +- * +- * Copy an extended attribute into the buffer +- * provided, or compute the buffer size required. +- * Buffer is NULL to compute the size of the buffer required. ++ * ext3_xattr_block_get() + * +- * Returns a negative error number on failure, or the number of bytes +- * used / required on success. ++ * routine looks for attribute in EA block and returns it's value and size + */ + int +-ext3_xattr_get(struct inode *inode, int name_index, const char *name, ++ext3_xattr_block_get(struct inode *inode, int name_index, const char *name, + void *buffer, size_t buffer_size) + { + struct buffer_head *bh = NULL; +@@ -447,6 +445,94 @@ + } + + /* ++ * ext3_xattr_ibody_get() ++ * ++ * routine looks for attribute in inode body and returns it's value and size ++ */ ++int ++ext3_xattr_ibody_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t buffer_size) ++{ ++ int size, name_len = strlen(name), storage_size; ++ struct ext3_xattr_entry *last; ++ struct ext3_inode *raw_inode; ++ struct ext3_iloc iloc; ++ char *start, *end; ++ int ret = -ENOENT; ++ ++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE) ++ return -ENOENT; ++ ++ ret = ext3_get_inode_loc(inode, &iloc); ++ if (ret) ++ return ret; ++ raw_inode = iloc.raw_inode; ++ ++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size - ++ EXT3_GOOD_OLD_INODE_SIZE - ++ EXT3_I(inode)->i_extra_isize - ++ sizeof(__u32); ++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE + ++ EXT3_I(inode)->i_extra_isize; ++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) { ++ brelse(iloc.bh); ++ return -ENOENT; ++ } ++ start += sizeof(__u32); ++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size; ++ ++ last = (struct ext3_xattr_entry *) start; ++ while (!IS_LAST_ENTRY(last)) { ++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last); ++ if (le32_to_cpu(last->e_value_size) > storage_size || ++ (char *) next >= end) { ++ ext3_error(inode->i_sb, "ext3_xattr_ibody_get", ++ "inode %ld", inode->i_ino); ++ brelse(iloc.bh); ++ return -EIO; ++ } ++ if (name_index == last->e_name_index && ++ name_len == last->e_name_len && ++ !memcmp(name, last->e_name, name_len)) ++ goto found; ++ last = next; ++ } ++ ++ /* can't find EA */ ++ brelse(iloc.bh); ++ return -ENOENT; ++ ++found: ++ size = le32_to_cpu(last->e_value_size); ++ if (buffer) { ++ ret = -ERANGE; ++ if (buffer_size >= size) { ++ memcpy(buffer, start + le16_to_cpu(last->e_value_offs), ++ size); ++ ret = size; ++ } ++ } else ++ ret = size; ++ brelse(iloc.bh); ++ return ret; ++} ++ ++int ext3_xattr_get(struct inode *inode, int name_index, const char *name, ++ void *buffer, size_t buffer_size) ++{ ++ int err; ++ ++ /* try to find attribute in inode body */ ++ err = ext3_xattr_ibody_get(inode, name_index, name, ++ buffer, buffer_size); ++ if (err < 0) ++ /* search was unsuccessful, try to find EA in dedicated block */ ++ err = ext3_xattr_block_get(inode, name_index, name, ++ buffer, buffer_size); ++ return err; ++} ++ ++/* + * ext3_xattr_list() + * + * Copy a list of attribute names into the buffer +@@ -457,7 +543,7 @@ + * used / required on success. + */ + int +-ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) ++ext3_xattr_block_list(struct inode *inode, char *buffer, size_t buffer_size) + { + struct buffer_head *bh = NULL; + struct ext3_xattr_entry *entry; +@@ -530,6 +616,131 @@ + return error; + } + ++/* ext3_xattr_ibody_list() ++ * ++ * generate list of attributes stored in inode body ++ */ ++int ++ext3_xattr_ibody_list(struct inode *inode, char *buffer, size_t buffer_size) ++{ ++ struct ext3_xattr_entry *last; ++ struct ext3_inode *raw_inode; ++ char *start, *end, *buf; ++ struct ext3_iloc iloc; ++ int storage_size; ++ int ret; ++ int size = 0; ++ ++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE) ++ return 0; ++ ++ ret = ext3_get_inode_loc(inode, &iloc); ++ if (ret) ++ return ret; ++ raw_inode = iloc.raw_inode; ++ ++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size - ++ EXT3_GOOD_OLD_INODE_SIZE - ++ EXT3_I(inode)->i_extra_isize - ++ sizeof(__u32); ++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE + ++ EXT3_I(inode)->i_extra_isize; ++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) { ++ brelse(iloc.bh); ++ return 0; ++ } ++ start += sizeof(__u32); ++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size; ++ ++ last = (struct ext3_xattr_entry *) start; ++ while (!IS_LAST_ENTRY(last)) { ++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last); ++ struct ext3_xattr_handler *handler; ++ if (le32_to_cpu(last->e_value_size) > storage_size || ++ (char *) next >= end) { ++ ext3_error(inode->i_sb, "ext3_xattr_ibody_list", ++ "inode %ld", inode->i_ino); ++ brelse(iloc.bh); ++ return -EIO; ++ } ++ handler = ext3_xattr_handler(last->e_name_index); ++ if (handler) ++ size += handler->list(NULL, inode, last->e_name, ++ last->e_name_len); ++ last = next; ++ } ++ ++ if (!buffer) { ++ ret = size; ++ goto cleanup; ++ } else { ++ ret = -ERANGE; ++ if (size > buffer_size) ++ goto cleanup; ++ } ++ ++ last = (struct ext3_xattr_entry *) start; ++ buf = buffer; ++ while (!IS_LAST_ENTRY(last)) { ++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last); ++ struct ext3_xattr_handler *handler; ++ handler = ext3_xattr_handler(last->e_name_index); ++ if (handler) ++ buf += handler->list(buf, inode, last->e_name, ++ last->e_name_len); ++ last = next; ++ } ++ ret = size; ++cleanup: ++ brelse(iloc.bh); ++ return ret; ++} ++ ++/* ++ * ext3_xattr_list() ++ * ++ * Copy a list of attribute names into the buffer ++ * provided, or compute the buffer size required. ++ * Buffer is NULL to compute the size of the buffer required. ++ * ++ * Returns a negative error number on failure, or the number of bytes ++ * used / required on success. ++ */ ++int ++ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size) ++{ ++ int error; ++ int size = buffer_size; ++ ++ /* get list of attributes stored in inode body */ ++ error = ext3_xattr_ibody_list(inode, buffer, buffer_size); ++ if (error < 0) { ++ /* some error occured while collecting ++ * attributes in inode body */ ++ size = 0; ++ goto cleanup; ++ } ++ size = error; ++ ++ /* get list of attributes stored in dedicated block */ ++ if (buffer) { ++ buffer_size -= error; ++ if (buffer_size <= 0) { ++ buffer = NULL; ++ buffer_size = 0; ++ } else ++ buffer += error; ++ } ++ ++ error = ext3_xattr_block_list(inode, buffer, buffer_size); ++ if (error < 0) ++ /* listing was successful, so we return len */ ++ size = 0; ++ ++cleanup: ++ return error + size; ++} ++ + /* + * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is + * not set, set it. +@@ -553,6 +764,279 @@ + } + + /* ++ * ext3_xattr_ibody_find() ++ * ++ * search attribute and calculate free space in inode body ++ * NOTE: free space includes space our attribute hold ++ */ ++int ++ext3_xattr_ibody_find(struct inode *inode, int name_index, ++ const char *name, struct ext3_xattr_entry *rentry, int *free) ++{ ++ struct ext3_xattr_entry *last; ++ struct ext3_inode *raw_inode; ++ int name_len = strlen(name); ++ int err, storage_size; ++ struct ext3_iloc iloc; ++ char *start, *end; ++ int ret = -ENOENT; ++ ++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE) ++ return ret; ++ ++ err = ext3_get_inode_loc(inode, &iloc); ++ if (err) ++ return -EIO; ++ raw_inode = iloc.raw_inode; ++ ++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size - ++ EXT3_GOOD_OLD_INODE_SIZE - ++ EXT3_I(inode)->i_extra_isize - ++ sizeof(__u32); ++ *free = storage_size - sizeof(__u32); ++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE + ++ EXT3_I(inode)->i_extra_isize; ++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) { ++ brelse(iloc.bh); ++ return -ENOENT; ++ } ++ start += sizeof(__u32); ++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size; ++ ++ last = (struct ext3_xattr_entry *) start; ++ while (!IS_LAST_ENTRY(last)) { ++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last); ++ if (le32_to_cpu(last->e_value_size) > storage_size || ++ (char *) next >= end) { ++ ext3_error(inode->i_sb, "ext3_xattr_ibody_find", ++ "inode %ld", inode->i_ino); ++ brelse(iloc.bh); ++ return -EIO; ++ } ++ ++ if (name_index == last->e_name_index && ++ name_len == last->e_name_len && ++ !memcmp(name, last->e_name, name_len)) { ++ memcpy(rentry, last, sizeof(struct ext3_xattr_entry)); ++ ret = 0; ++ } else { ++ *free -= EXT3_XATTR_LEN(last->e_name_len); ++ *free -= le32_to_cpu(last->e_value_size); ++ } ++ last = next; ++ } ++ ++ brelse(iloc.bh); ++ return ret; ++} ++ ++/* ++ * ext3_xattr_block_find() ++ * ++ * search attribute and calculate free space in EA block (if it allocated) ++ * NOTE: free space includes space our attribute hold ++ */ ++int ++ext3_xattr_block_find(struct inode *inode, int name_index, const char *name, ++ struct ext3_xattr_entry *rentry, int *free) ++{ ++ struct buffer_head *bh = NULL; ++ struct ext3_xattr_entry *entry; ++ char *end; ++ int name_len, error = -ENOENT; ++ ++ if (!EXT3_I(inode)->i_file_acl) { ++ *free = inode->i_sb->s_blocksize - ++ sizeof(struct ext3_xattr_header) - ++ sizeof(__u32); ++ return -ENOENT; ++ } ++ ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl); ++ bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl); ++ if (!bh) ++ return -EIO; ++ ea_bdebug(bh, "b_count=%d, refcount=%d", ++ atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount)); ++ end = bh->b_data + bh->b_size; ++ if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) || ++ HDR(bh)->h_blocks != cpu_to_le32(1)) { ++bad_block: ext3_error(inode->i_sb, "ext3_xattr_get", ++ "inode %ld: bad block %d", inode->i_ino, ++ EXT3_I(inode)->i_file_acl); ++ brelse(bh); ++ return -EIO; ++ } ++ /* find named attribute */ ++ name_len = strlen(name); ++ *free = bh->b_size - sizeof(__u32); ++ ++ entry = FIRST_ENTRY(bh); ++ while (!IS_LAST_ENTRY(entry)) { ++ struct ext3_xattr_entry *next = ++ EXT3_XATTR_NEXT(entry); ++ if ((char *)next >= end) ++ goto bad_block; ++ if (name_index == entry->e_name_index && ++ name_len == entry->e_name_len && ++ memcmp(name, entry->e_name, name_len) == 0) { ++ memcpy(rentry, entry, sizeof(struct ext3_xattr_entry)); ++ error = 0; ++ } else { ++ *free -= EXT3_XATTR_LEN(entry->e_name_len); ++ *free -= le32_to_cpu(entry->e_value_size); ++ } ++ entry = next; ++ } ++ brelse(bh); ++ ++ return error; ++} ++ ++/* ++ * ext3_xattr_inode_set() ++ * ++ * this routine add/remove/replace attribute in inode body ++ */ ++int ++ext3_xattr_ibody_set(handle_t *handle, struct inode *inode, int name_index, ++ const char *name, const void *value, size_t value_len, ++ int flags) ++{ ++ struct ext3_xattr_entry *last, *next, *here = NULL; ++ struct ext3_inode *raw_inode; ++ int name_len = strlen(name); ++ int esize = EXT3_XATTR_LEN(name_len); ++ struct buffer_head *bh; ++ int err, storage_size; ++ struct ext3_iloc iloc; ++ int free, min_offs; ++ char *start, *end; ++ ++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE) ++ return -ENOSPC; ++ ++ err = ext3_get_inode_loc(inode, &iloc); ++ if (err) ++ return err; ++ raw_inode = iloc.raw_inode; ++ bh = iloc.bh; ++ ++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size - ++ EXT3_GOOD_OLD_INODE_SIZE - ++ EXT3_I(inode)->i_extra_isize - ++ sizeof(__u32); ++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE + ++ EXT3_I(inode)->i_extra_isize; ++ if ((*(__u32*) start) != EXT3_XATTR_MAGIC) { ++ /* inode had no attributes before */ ++ *((__u32*) start) = cpu_to_le32(EXT3_XATTR_MAGIC); ++ } ++ start += sizeof(__u32); ++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size; ++ min_offs = storage_size; ++ free = storage_size - sizeof(__u32); ++ ++ last = (struct ext3_xattr_entry *) start; ++ while (!IS_LAST_ENTRY(last)) { ++ next = EXT3_XATTR_NEXT(last); ++ if (le32_to_cpu(last->e_value_size) > storage_size || ++ (char *) next >= end) { ++ ext3_error(inode->i_sb, "ext3_xattr_ibody_set", ++ "inode %ld", inode->i_ino); ++ brelse(bh); ++ return -EIO; ++ } ++ ++ if (last->e_value_size) { ++ int offs = le16_to_cpu(last->e_value_offs); ++ if (offs < min_offs) ++ min_offs = offs; ++ } ++ if (name_index == last->e_name_index && ++ name_len == last->e_name_len && ++ !memcmp(name, last->e_name, name_len)) ++ here = last; ++ else { ++ /* we calculate all but our attribute ++ * because it will be removed before changing */ ++ free -= EXT3_XATTR_LEN(last->e_name_len); ++ free -= le32_to_cpu(last->e_value_size); ++ } ++ last = next; ++ } ++ ++ if (value && (esize + value_len > free)) { ++ brelse(bh); ++ return -ENOSPC; ++ } ++ ++ err = ext3_reserve_inode_write(handle, inode, &iloc); ++ if (err) { ++ brelse(bh); ++ return err; ++ } ++ ++ if (here) { ++ /* time to remove old value */ ++ struct ext3_xattr_entry *e; ++ int size = le32_to_cpu(here->e_value_size); ++ int border = le16_to_cpu(here->e_value_offs); ++ char *src; ++ ++ /* move tail */ ++ memmove(start + min_offs + size, start + min_offs, ++ border - min_offs); ++ ++ /* recalculate offsets */ ++ e = (struct ext3_xattr_entry *) start; ++ while (!IS_LAST_ENTRY(e)) { ++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(e); ++ int offs = le16_to_cpu(e->e_value_offs); ++ if (offs < border) ++ e->e_value_offs = ++ cpu_to_le16(offs + size); ++ e = next; ++ } ++ min_offs += size; ++ ++ /* remove entry */ ++ border = EXT3_XATTR_LEN(here->e_name_len); ++ src = (char *) here + EXT3_XATTR_LEN(here->e_name_len); ++ size = (char *) last - src; ++ if ((char *) here + size > end) ++ printk("ALERT at %s:%d: 0x%p + %d > 0x%p\n", ++ __FILE__, __LINE__, here, size, end); ++ memmove(here, src, size); ++ last = (struct ext3_xattr_entry *) ((char *) last - border); ++ *((__u32 *) last) = 0; ++ } ++ ++ if (value) { ++ int offs = min_offs - value_len; ++ /* use last to create new entry */ ++ last->e_name_len = strlen(name); ++ last->e_name_index = name_index; ++ last->e_value_offs = cpu_to_le16(offs); ++ last->e_value_size = cpu_to_le32(value_len); ++ last->e_hash = last->e_value_block = 0; ++ memset(last->e_name, 0, esize); ++ memcpy(last->e_name, name, last->e_name_len); ++ if (start + offs + value_len > end) ++ printk("ALERT at %s:%d: 0x%p + %d + %d > 0x%p\n", ++ __FILE__, __LINE__, start, offs, ++ value_len, end); ++ memcpy(start + offs, value, value_len); ++ last = EXT3_XATTR_NEXT(last); ++ *((__u32 *) last) = 0; ++ } ++ ++ ext3_mark_iloc_dirty(handle, inode, &iloc); ++ brelse(bh); ++ ++ return 0; ++} ++ ++/* + * ext3_xattr_set() + * + * Create, replace or remove an extended attribute for this inode. Buffer +@@ -566,6 +1050,101 @@ + */ + int + ext3_xattr_set(handle_t *handle, struct inode *inode, int name_index, ++ const char *name, const void *value, size_t value_len, int flags) ++{ ++ struct ext3_xattr_entry entry; ++ int err, where = 0, found = 0, total; ++ int free1 = -1, free2 = -1; ++ int name_len; ++ ++ ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld", ++ name_index, name, value, (long)value_len); ++ ++ if (IS_RDONLY(inode)) ++ return -EROFS; ++ if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) ++ return -EPERM; ++ if (value == NULL) ++ value_len = 0; ++ if (name == NULL) ++ return -EINVAL; ++ name_len = strlen(name); ++ if (name_len > 255 || value_len > inode->i_sb->s_blocksize) ++ return -ERANGE; ++ ++ /* try to find attribute in inode body */ ++ err = ext3_xattr_ibody_find(inode, name_index, name, &entry, &free1); ++ if (err == 0) { ++ /* found EA in inode */ ++ found = 1; ++ where = 0; ++ } else if (err == -ENOENT) { ++ /* there is no such attribute in inode body */ ++ /* try to find attribute in dedicated block */ ++ err = ext3_xattr_block_find(inode, name_index, name, ++ &entry, &free2); ++ if (err != 0 && err != -ENOENT) { ++ /* not found EA in block */ ++ goto finish; ++ } else if (err == 0) { ++ /* found EA in block */ ++ where = 1; ++ found = 1; ++ } ++ } else ++ goto finish; ++ ++ /* check flags: may replace? may create ? */ ++ if (found && (flags & XATTR_CREATE)) { ++ err = -EEXIST; ++ goto finish; ++ } else if (!found && (flags & XATTR_REPLACE)) { ++ err = -ENODATA; ++ goto finish; ++ } ++ ++ /* check if we have enough space to store attribute */ ++ total = EXT3_XATTR_LEN(strlen(name)) + value_len; ++ if (free1 >= 0 && total > free1 && free2 >= 0 && total > free2) { ++ /* have no enough space */ ++ err = -ENOSPC; ++ goto finish; ++ } ++ ++ /* time to remove attribute */ ++ if (found) { ++ if (where == 0) { ++ /* EA is stored in inode body */ ++ ext3_xattr_ibody_set(handle, inode, name_index, name, ++ NULL, 0, flags); ++ } else { ++ /* EA is stored in separated block */ ++ ext3_xattr_block_set(handle, inode, name_index, name, ++ NULL, 0, flags); ++ } ++ } ++ ++ /* try to store EA in inode body */ ++ err = ext3_xattr_ibody_set(handle, inode, name_index, name, ++ value, value_len, flags); ++ if (err) { ++ /* can't store EA in inode body */ ++ /* try to store in block */ ++ err = ext3_xattr_block_set(handle, inode, name_index, ++ name, value, value_len, flags); ++ } ++ ++finish: ++ return err; ++} ++ ++/* ++ * ext3_xattr_block_set() ++ * ++ * this routine add/remove/replace attribute in EA block ++ */ ++int ++ext3_xattr_block_set(handle_t *handle, struct inode *inode, int name_index, + const char *name, const void *value, size_t value_len, int flags) + { + struct super_block *sb = inode->i_sb; +@@ -603,6 +1182,7 @@ + name_len = strlen(name); + if (name_len > 255 || value_len > sb->s_blocksize) + return -ERANGE; ++ + down(&ext3_xattr_sem); + + if (block) { +Index: linux-2.4.29/include/linux/ext3_fs.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs.h 2005-05-03 15:58:30.767273592 +0300 ++++ linux-2.4.29/include/linux/ext3_fs.h 2005-05-03 16:07:33.009840192 +0300 +@@ -259,6 +259,8 @@ + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ ++ __u16 i_extra_isize; ++ __u16 i_pad1; + }; + + #define i_size_high i_dir_acl +Index: linux-2.4.29/include/linux/ext3_fs_i.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs_i.h 2005-04-07 18:52:18.000000000 +0300 ++++ linux-2.4.29/include/linux/ext3_fs_i.h 2005-05-03 16:07:33.010840040 +0300 +@@ -62,6 +62,9 @@ + */ + loff_t i_disksize; + ++ /* on-disk additional length */ ++ __u16 i_extra_isize; ++ + /* + * truncate_sem is for serialising ext3_truncate() against + * ext3_getblock(). In the 2.4 ext2 design, great chunks of inode's diff --git a/lustre/kernel_patches/patches/ext3-extents-2.4.21-chaos.patch b/lustre/kernel_patches/patches/ext3-extents-2.4.21-chaos.patch index 3b4d07b..5ef713b 100644 --- a/lustre/kernel_patches/patches/ext3-extents-2.4.21-chaos.patch +++ b/lustre/kernel_patches/patches/ext3-extents-2.4.21-chaos.patch @@ -2526,9 +2526,9 @@ Index: linux-2.4.21-rhel/include/linux/ext3_fs.h #ifdef CONFIG_JBD_DEBUG #define EXT3_IOC_WAIT_FOR_READONLY _IOR('f', 99, long) #endif -+#define EXT3_IOC_GET_EXTENTS _IOR('f', 5, long) -+#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 6, long) -+#define EXT3_IOC_GET_TREE_STATS _IOR('f', 7, long) ++#define EXT3_IOC_GET_EXTENTS _IOR('f', 7, long) ++#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 8, long) ++#define EXT3_IOC_GET_TREE_STATS _IOR('f', 9, long) /* * Structure of an inode on the disk diff --git a/lustre/kernel_patches/patches/ext3-extents-2.4.21-suse2.patch b/lustre/kernel_patches/patches/ext3-extents-2.4.21-suse2.patch index eb295e1..53cb38e 100644 --- a/lustre/kernel_patches/patches/ext3-extents-2.4.21-suse2.patch +++ b/lustre/kernel_patches/patches/ext3-extents-2.4.21-suse2.patch @@ -2461,17 +2461,17 @@ Index: linux-2.4.21-suse2/fs/ext3/Makefile export-objs += xattr.o Index: linux-2.4.21-suse2/fs/ext3/super.c =================================================================== ---- linux-2.4.21-suse2.orig/fs/ext3/super.c 2004-11-02 20:31:37.000000000 +0300 -+++ linux-2.4.21-suse2/fs/ext3/super.c 2004-11-02 20:31:39.000000000 +0300 -@@ -624,6 +624,7 @@ - int i; - +--- linux-2.4.21-suse2.orig/fs/ext3/super.c 2005-04-04 05:44:58.000000000 -0600 ++++ linux-2.4.21-suse2/fs/ext3/super.c 2005-04-04 05:45:43.000000000 -0600 +@@ -532,6 +532,7 @@ + #ifdef EXT3_DELETE_THREAD J_ASSERT(sbi->s_delete_inodes == 0); + #endif + ext3_ext_release(sb); ext3_xattr_put_super(sb); journal_destroy(sbi->s_journal); if (!(sb->s_flags & MS_RDONLY)) { -@@ -829,6 +830,10 @@ +@@ -733,6 +734,10 @@ return 0; } } @@ -2482,7 +2482,7 @@ Index: linux-2.4.21-suse2/fs/ext3/super.c else if (!strcmp (this_char, "grpid") || !strcmp (this_char, "bsdgroups")) set_opt (*mount_options, GRPID); -@@ -1524,6 +1529,8 @@ +@@ -1428,6 +1433,8 @@ test_opt(sb,DATA_FLAGS) == EXT3_MOUNT_ORDERED_DATA ? "ordered": "writeback"); @@ -2525,9 +2525,9 @@ Index: linux-2.4.21-suse2/include/linux/ext3_fs.h #ifdef CONFIG_JBD_DEBUG #define EXT3_IOC_WAIT_FOR_READONLY _IOR('f', 99, long) #endif -+#define EXT3_IOC_GET_EXTENTS _IOR('f', 5, long) -+#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 6, long) -+#define EXT3_IOC_GET_TREE_STATS _IOR('f', 7, long) ++#define EXT3_IOC_GET_EXTENTS _IOR('f', 7, long) ++#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 8, long) ++#define EXT3_IOC_GET_TREE_STATS _IOR('f', 9, long) /* * Structure of an inode on the disk diff --git a/lustre/kernel_patches/patches/ext3-extents-2.4.24.patch b/lustre/kernel_patches/patches/ext3-extents-2.4.24.patch index 81dc7ad..3364139 100644 --- a/lustre/kernel_patches/patches/ext3-extents-2.4.24.patch +++ b/lustre/kernel_patches/patches/ext3-extents-2.4.24.patch @@ -2513,9 +2513,9 @@ Index: linux-2.4.24/include/linux/ext3_fs.h #ifdef CONFIG_JBD_DEBUG #define EXT3_IOC_WAIT_FOR_READONLY _IOR('f', 99, long) #endif -+#define EXT3_IOC_GET_EXTENTS _IOR('f', 5, long) -+#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 6, long) -+#define EXT3_IOC_GET_TREE_STATS _IOR('f', 7, long) ++#define EXT3_IOC_GET_EXTENTS _IOR('f', 7, long) ++#define EXT3_IOC_GET_TREE_DEPTH _IOR('f', 8, long) ++#define EXT3_IOC_GET_TREE_STATS _IOR('f', 9, long) /* * Structure of an inode on the disk diff --git a/lustre/kernel_patches/patches/ext3-extents-2.4.29.patch b/lustre/kernel_patches/patches/ext3-extents-2.4.29.patch new file mode 100644 index 0000000..11acf5c --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-extents-2.4.29.patch @@ -0,0 +1,2851 @@ +Index: linux-2.4.29/fs/ext3/extents.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/extents.c 2005-05-03 16:52:08.723069952 +0300 ++++ linux-2.4.29/fs/ext3/extents.c 2005-05-03 16:52:08.802057944 +0300 +@@ -0,0 +1,2302 @@ ++/* ++ * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com ++ * Written by Alex Tomas ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++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 ++ * ++ * 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 */ diff --git a/lustre/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch b/lustre/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch index c8fc99a..3b873c2 100644 --- a/lustre/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch +++ b/lustre/kernel_patches/patches/ext3-extents-2.6.9-rhel4.patch @@ -2556,16 +2556,16 @@ Index: linux-stage/include/linux/ext3_fs.h --- linux-stage.orig/include/linux/ext3_fs.h 2005-02-25 14:53:56.424908168 +0200 +++ linux-stage/include/linux/ext3_fs.h 2005-02-25 15:39:12.841950008 +0200 @@ -186,8 +186,9 @@ + #define EXT3_NOTAIL_FL 0x00008000 /* don't merge file tail */ #define EXT3_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ #define EXT3_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ - #define EXT3_RESERVED_FL 0x80000000 /* reserved for ext3 lib */ +#define EXT3_EXTENTS_FL 0x00080000 /* Inode uses extents */ + #define EXT3_RESERVED_FL 0x80000000 /* reserved for ext3 lib */ -#define EXT3_FL_USER_VISIBLE 0x0003DFFF /* User visible flags */ +#define EXT3_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ #define EXT3_FL_USER_MODIFIABLE 0x000380FF /* User modifiable flags */ - /* @@ -237,6 +238,9 @@ #endif #define EXT3_IOC_GETRSVSZ _IOR('f', 5, long) diff --git a/lustre/kernel_patches/patches/ext3-htree-2.4.21-rhel.patch b/lustre/kernel_patches/patches/ext3-htree-2.4.21-rhel.patch index 5fb5951..c42156b 100644 --- a/lustre/kernel_patches/patches/ext3-htree-2.4.21-rhel.patch +++ b/lustre/kernel_patches/patches/ext3-htree-2.4.21-rhel.patch @@ -1328,7 +1328,7 @@ Index: linux-2.4.21/fs/ext3/namei.c ret = bh; goto cleanup_and_exit; } else { -@@ -198,6 +863,66 @@ +@@ -198,6 +863,74 @@ return ret; } @@ -1347,17 +1347,25 @@ Index: linux-2.4.21/fs/ext3/namei.c + int namelen = dentry->d_name.len; + const u8 *name = dentry->d_name.name; + struct inode *dir = dentry->d_parent->d_inode; -+ ++ + sb = dir->i_sb; -+ if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) -+ return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); + if (!(bh = ext3_bread (NULL,dir, block, 0, err))) + goto errout; + de = (struct ext3_dir_entry_2 *) bh->b_data; -+ top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize - ++ top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize - + EXT3_DIR_REC_LEN(0)); + for (; de < top; de = ext3_next_entry(de)) + if (ext3_match (namelen, name, de)) { @@ -2521,16 +2529,3 @@ Index: linux-2.4.21/include/linux/ext3_jbd.h int ext3_mark_iloc_dirty(handle_t *handle, struct inode *inode, -Index: linux-2.4.21/lib/rbtree.c -=================================================================== ---- linux-2.4.21.orig/lib/rbtree.c 2004-09-11 10:16:18.000000000 -0400 -+++ linux-2.4.21/lib/rbtree.c 2004-09-16 19:40:16.000000000 -0400 -@@ -17,6 +17,8 @@ - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - linux/lib/rbtree.c -+ -+ rb_get_first and rb_get_next written by Theodore Ts'o, 9/8/2002 - */ - - #include diff --git a/lustre/kernel_patches/patches/ext3-htree-2.4.22-rh.patch b/lustre/kernel_patches/patches/ext3-htree-2.4.22-rh.patch index ca2cacf..7895513 100644 --- a/lustre/kernel_patches/patches/ext3-htree-2.4.22-rh.patch +++ b/lustre/kernel_patches/patches/ext3-htree-2.4.22-rh.patch @@ -1318,7 +1318,7 @@ ret = bh; goto cleanup_and_exit; } else { -@@ -197,6 +862,66 @@ cleanup_and_exit: +@@ -197,6 +862,74 @@ cleanup_and_exit: return ret; } @@ -1337,17 +1337,25 @@ + int namelen = dentry->d_name.len; + const u8 *name = dentry->d_name.name; + struct inode *dir = dentry->d_parent->d_inode; -+ ++ + sb = dir->i_sb; -+ if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) -+ return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, 0, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); + if (!(bh = ext3_bread (NULL,dir, block, 0, err))) + goto errout; + de = (struct ext3_dir_entry_2 *) bh->b_data; -+ top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize - ++ top = (struct ext3_dir_entry_2 *)((char *)de + sb->s_blocksize - + EXT3_DIR_REC_LEN(0)); + for (; de < top; de = ext3_next_entry(de)) + if (ext3_match (namelen, name, de)) { diff --git a/lustre/kernel_patches/patches/ext3-htree-2.4.29.patch b/lustre/kernel_patches/patches/ext3-htree-2.4.29.patch new file mode 100644 index 0000000..259c7b7 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-htree-2.4.29.patch @@ -0,0 +1,2496 @@ +Index: linux-2.4.29/fs/ext3/dir.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/dir.c 2005-04-07 18:53:53.000000000 +0300 ++++ linux-2.4.29/fs/ext3/dir.c 2005-05-03 16:34:05.481747664 +0300 +@@ -21,12 +21,16 @@ + #include + #include + #include ++#include ++#include + + 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 ++#include ++#include ++#include ++ ++#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 +@@ -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<b_data))) { ++ brelse (bh); ++ goto errout; ++ } ++ *res_dir = de; ++ dx_release (frames); ++ return bh; ++ } ++ brelse (bh); ++ /* Check to see if we should continue to search */ ++ retval = ext3_htree_next_block(dir, hash, frame, ++ frames, err, 0); ++ if (retval == -1) { ++ ext3_warning(sb, __FUNCTION__, ++ "error reading index page in directory #%lu", ++ dir->i_ino); ++ goto errout; ++ } ++ } while (retval == 1); ++ ++ *err = -ENOENT; ++errout: ++ dxtrace(printk("%s not found\n", name)); ++ dx_release (frames); ++ return NULL; ++} ++#endif ++ + static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry) + { + struct inode * inode; +@@ -212,8 +937,9 @@ + brelse (bh); + inode = iget(dir->i_sb, ino); + +- if (!inode) ++ if (!inode) { + return ERR_PTR(-EACCES); ++ } + } + d_add(dentry, inode); + return NULL; +@@ -237,6 +963,301 @@ + de->file_type = ext3_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; + } + ++#ifdef CONFIG_EXT3_INDEX ++static struct ext3_dir_entry_2 * ++dx_move_dirents(char *from, char *to, struct dx_map_entry *map, int count) ++{ ++ unsigned rec_len = 0; ++ ++ while (count--) { ++ struct ext3_dir_entry_2 *de = (struct ext3_dir_entry_2 *) (from + map->offs); ++ rec_len = EXT3_DIR_REC_LEN(de->name_len); ++ memcpy (to, de, rec_len); ++ ((struct ext3_dir_entry_2 *)to)->rec_len = cpu_to_le16(rec_len); ++ de->inode = 0; ++ map++; ++ to += rec_len; ++ } ++ return (struct ext3_dir_entry_2 *) (to - rec_len); ++} ++ ++static struct ext3_dir_entry_2* dx_pack_dirents(char *base, int size) ++{ ++ struct ext3_dir_entry_2 *next, *to, *prev, *de = (struct ext3_dir_entry_2 *) base; ++ unsigned rec_len = 0; ++ ++ prev = to = de; ++ while ((char*)de < base + size) { ++ next = (struct ext3_dir_entry_2 *) ((char *) de + ++ le16_to_cpu(de->rec_len)); ++ if (de->inode && de->name_len) { ++ rec_len = EXT3_DIR_REC_LEN(de->name_len); ++ if (de > to) ++ memmove(to, de, rec_len); ++ to->rec_len = cpu_to_le16(rec_len); ++ prev = to; ++ to = (struct ext3_dir_entry_2 *)((char *)to + rec_len); ++ } ++ de = next; ++ } ++ return prev; ++} ++ ++static struct ext3_dir_entry_2 *do_split(handle_t *handle, struct inode *dir, ++ struct buffer_head **bh,struct dx_frame *frame, ++ struct dx_hash_info *hinfo, int *error) ++{ ++ unsigned blocksize = dir->i_sb->s_blocksize; ++ unsigned count, continued; ++ struct buffer_head *bh2; ++ u32 newblock; ++ u32 hash2; ++ struct dx_map_entry *map; ++ char *data1 = (*bh)->b_data, *data2; ++ unsigned split; ++ struct ext3_dir_entry_2 *de = NULL, *de2; ++ int err; ++ ++ bh2 = ext3_append (handle, dir, &newblock, error); ++ if (!(bh2)) { ++ brelse(*bh); ++ *bh = NULL; ++ goto errout; ++ } ++ ++ BUFFER_TRACE(*bh, "get_write_access"); ++ err = ext3_journal_get_write_access(handle, *bh); ++ if (err) { ++ journal_error: ++ brelse(*bh); ++ brelse(bh2); ++ *bh = NULL; ++ ext3_std_error(dir->i_sb, err); ++ goto errout; ++ } ++ BUFFER_TRACE(frame->bh, "get_write_access"); ++ err = ext3_journal_get_write_access(handle, frame->bh); ++ if (err) ++ goto journal_error; ++ ++ data2 = bh2->b_data; ++ ++ /* create map in the end of data2 block */ ++ map = (struct dx_map_entry *) (data2 + blocksize); ++ count = dx_make_map ((struct ext3_dir_entry_2 *) data1, ++ blocksize, hinfo, map); ++ map -= count; ++ split = count/2; // need to adjust to actual middle ++ dx_sort_map (map, count); ++ hash2 = map[split].hash; ++ continued = hash2 == map[split - 1].hash; ++ dxtrace(printk("Split block %i at %x, %i/%i\n", ++ dx_get_block(frame->at), hash2, split, count-split)); ++ ++ /* Fancy dance to stay within two buffers */ ++ de2 = dx_move_dirents(data1, data2, map + split, count - split); ++ de = dx_pack_dirents(data1,blocksize); ++ de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); ++ de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2); ++ dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data1, blocksize, 1)); ++ dxtrace(dx_show_leaf (hinfo, (struct ext3_dir_entry_2 *) data2, blocksize, 1)); ++ ++ /* Which block gets the new entry? */ ++ if (hinfo->hash >= hash2) ++ { ++ swap(*bh, bh2); ++ de = de2; ++ } ++ dx_insert_block (frame, hash2 + continued, newblock); ++ err = ext3_journal_dirty_metadata (handle, bh2); ++ if (err) ++ goto journal_error; ++ err = ext3_journal_dirty_metadata (handle, frame->bh); ++ if (err) ++ goto journal_error; ++ brelse (bh2); ++ dxtrace(dx_show_index ("frame", frame->entries)); ++errout: ++ return de; ++} ++#endif ++ ++ ++/* ++ * Add a new entry into a directory (leaf) block. If de is non-NULL, ++ * it points to a directory entry which is guaranteed to be large ++ * enough for new directory entry. If de is NULL, then ++ * add_dirent_to_buf will attempt search the directory block for ++ * space. It will return -ENOSPC if no space is available, and -EIO ++ * and -EEXIST if directory entry already exists. ++ * ++ * NOTE! bh is NOT released in the case where ENOSPC is returned. In ++ * all other cases bh is released. ++ */ ++static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, ++ struct inode *inode, struct ext3_dir_entry_2 *de, ++ struct buffer_head * bh) ++{ ++ struct inode *dir = dentry->d_parent->d_inode; ++ const char *name = dentry->d_name.name; ++ int namelen = dentry->d_name.len; ++ unsigned long offset = 0; ++ unsigned short reclen; ++ int nlen, rlen, err; ++ char *top; ++ ++ reclen = EXT3_DIR_REC_LEN(namelen); ++ if (!de) { ++ de = (struct ext3_dir_entry_2 *)bh->b_data; ++ top = bh->b_data + dir->i_sb->s_blocksize - reclen; ++ while ((char *) de <= top) { ++ if (!ext3_check_dir_entry("ext3_add_entry", dir, de, ++ bh, offset)) { ++ brelse (bh); ++ return -EIO; ++ } ++ if (ext3_match (namelen, name, de)) { ++ brelse (bh); ++ return -EEXIST; ++ } ++ nlen = EXT3_DIR_REC_LEN(de->name_len); ++ rlen = le16_to_cpu(de->rec_len); ++ if ((de->inode? rlen - nlen: rlen) >= reclen) ++ break; ++ de = (struct ext3_dir_entry_2 *)((char *)de + rlen); ++ offset += rlen; ++ } ++ if ((char *) de > top) ++ return -ENOSPC; ++ } ++ BUFFER_TRACE(bh, "get_write_access"); ++ err = ext3_journal_get_write_access(handle, bh); ++ if (err) { ++ ext3_std_error(dir->i_sb, err); ++ brelse(bh); ++ return err; ++ } ++ ++ /* By now the buffer is marked for journaling */ ++ nlen = EXT3_DIR_REC_LEN(de->name_len); ++ rlen = le16_to_cpu(de->rec_len); ++ if (de->inode) { ++ struct ext3_dir_entry_2 *de1 = (struct ext3_dir_entry_2 *)((char *)de + nlen); ++ de1->rec_len = cpu_to_le16(rlen - nlen); ++ de->rec_len = cpu_to_le16(nlen); ++ de = de1; ++ } ++ de->file_type = EXT3_FT_UNKNOWN; ++ if (inode) { ++ de->inode = cpu_to_le32(inode->i_ino); ++ ext3_set_de_type(dir->i_sb, de, inode->i_mode); ++ } else ++ de->inode = 0; ++ de->name_len = namelen; ++ memcpy (de->name, name, namelen); ++ /* ++ * XXX shouldn't update any times until successful ++ * completion of syscall, but too many callers depend ++ * on this. ++ * ++ * XXX similarly, too many callers depend on ++ * ext3_new_inode() setting the times, but error ++ * recovery deletes the inode, so the worst that can ++ * happen is that the times are slightly out of date ++ * and/or different from the directory change time. ++ */ ++ dir->i_mtime = dir->i_ctime = CURRENT_TIME; ++ ext3_update_dx_flag(dir); ++ dir->i_version = ++event; ++ ext3_mark_inode_dirty(handle, dir); ++ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); ++ err = ext3_journal_dirty_metadata(handle, bh); ++ if (err) ++ ext3_std_error(dir->i_sb, err); ++ brelse(bh); ++ return 0; ++} ++ ++#ifdef CONFIG_EXT3_INDEX ++/* ++ * This converts a one block unindexed directory to a 3 block indexed ++ * directory, and adds the dentry to the indexed directory. ++ */ ++static int make_indexed_dir(handle_t *handle, struct dentry *dentry, ++ struct inode *inode, struct buffer_head *bh) ++{ ++ struct inode *dir = dentry->d_parent->d_inode; ++ const char *name = dentry->d_name.name; ++ int namelen = dentry->d_name.len; ++ struct buffer_head *bh2; ++ struct dx_root *root; ++ struct dx_frame frames[2], *frame; ++ struct dx_entry *entries; ++ struct ext3_dir_entry_2 *de, *de2; ++ char *data1, *top; ++ unsigned len; ++ int retval; ++ unsigned blocksize; ++ struct dx_hash_info hinfo; ++ u32 block; ++ ++ blocksize = dir->i_sb->s_blocksize; ++ dxtrace(printk("Creating index\n")); ++ retval = ext3_journal_get_write_access(handle, bh); ++ if (retval) { ++ ext3_std_error(dir->i_sb, retval); ++ brelse(bh); ++ return retval; ++ } ++ root = (struct dx_root *) bh->b_data; ++ ++ EXT3_I(dir)->i_flags |= EXT3_INDEX_FL; ++ bh2 = ext3_append (handle, dir, &block, &retval); ++ if (!(bh2)) { ++ brelse(bh); ++ return retval; ++ } ++ data1 = bh2->b_data; ++ ++ /* The 0th block becomes the root, move the dirents out */ ++ de = (struct ext3_dir_entry_2 *)&root->dotdot; ++ de = (struct ext3_dir_entry_2 *)((char *)de + le16_to_cpu(de->rec_len)); ++ len = ((char *) root) + blocksize - (char *) de; ++ memcpy (data1, de, len); ++ de = (struct ext3_dir_entry_2 *) data1; ++ top = data1 + len; ++ while (((char *) de2=(char*)de+le16_to_cpu(de->rec_len)) < top) ++ de = de2; ++ de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de); ++ /* Initialize the root; the dot dirents already exist */ ++ de = (struct ext3_dir_entry_2 *) (&root->dotdot); ++ de->rec_len = cpu_to_le16(blocksize - EXT3_DIR_REC_LEN(2)); ++ memset (&root->info, 0, sizeof(root->info)); ++ root->info.info_length = sizeof(root->info); ++ root->info.hash_version = dir->i_sb->u.ext3_sb.s_def_hash_version; ++ entries = root->entries; ++ dx_set_block (entries, 1); ++ dx_set_count (entries, 1); ++ dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info))); ++ ++ /* Initialize as for dx_probe */ ++ hinfo.hash_version = root->info.hash_version; ++ hinfo.seed = dir->i_sb->u.ext3_sb.s_hash_seed; ++ ext3fs_dirhash(name, namelen, &hinfo); ++ frame = frames; ++ frame->entries = entries; ++ frame->at = entries; ++ frame->bh = bh; ++ bh = bh2; ++ de = do_split(handle,dir, &bh, frame, &hinfo, &retval); ++ dx_release (frames); ++ if (!(de)) ++ return retval; ++ ++ return add_dirent_to_buf(handle, dentry, inode, de, bh); ++} ++#endif ++ + /* + * ext3_add_entry() + * +@@ -247,127 +1268,198 @@ + * may not sleep between calling this and putting something into + * the entry, as someone else might have used it while you slept. + */ +- +-/* +- * AKPM: the journalling code here looks wrong on the error paths +- */ + static int ext3_add_entry (handle_t *handle, struct dentry *dentry, + struct inode *inode) + { + struct inode *dir = dentry->d_parent->d_inode; +- const char *name = dentry->d_name.name; +- int namelen = dentry->d_name.len; + unsigned long offset; +- unsigned short rec_len; + struct buffer_head * bh; +- struct ext3_dir_entry_2 * de, * de1; ++ struct ext3_dir_entry_2 *de; + struct super_block * sb; + int retval; ++#ifdef CONFIG_EXT3_INDEX ++ int dx_fallback=0; ++#endif ++ unsigned blocksize; ++ unsigned nlen, rlen; ++ u32 block, blocks; + + sb = dir->i_sb; +- +- if (!namelen) ++ blocksize = sb->s_blocksize; ++ if (!dentry->d_name.len) + return -EINVAL; +- bh = ext3_bread (handle, dir, 0, 0, &retval); ++#ifdef CONFIG_EXT3_INDEX ++ if (is_dx(dir)) { ++ retval = ext3_dx_add_entry(handle, dentry, inode); ++ if (!retval || (retval != ERR_BAD_DX_DIR)) ++ return retval; ++ EXT3_I(dir)->i_flags &= ~EXT3_INDEX_FL; ++ dx_fallback++; ++ ext3_mark_inode_dirty(handle, dir); ++ } ++#endif ++ blocks = dir->i_size >> sb->s_blocksize_bits; ++ for (block = 0, offset = 0; block < blocks; block++) { ++ bh = ext3_bread(handle, dir, block, 0, &retval); ++ if(!bh) ++ return retval; ++ retval = add_dirent_to_buf(handle, dentry, inode, 0, bh); ++ if (retval != -ENOSPC) ++ return retval; ++ ++#ifdef CONFIG_EXT3_INDEX ++ if (blocks == 1 && !dx_fallback && ++ EXT3_HAS_COMPAT_FEATURE(sb, EXT3_FEATURE_COMPAT_DIR_INDEX)) ++ return make_indexed_dir(handle, dentry, inode, bh); ++#endif ++ brelse(bh); ++ } ++ bh = ext3_append(handle, dir, &block, &retval); + if (!bh) + return retval; +- rec_len = EXT3_DIR_REC_LEN(namelen); +- offset = 0; + de = (struct ext3_dir_entry_2 *) bh->b_data; +- while (1) { +- if ((char *)de >= sb->s_blocksize + bh->b_data) { +- brelse (bh); +- bh = NULL; +- bh = ext3_bread (handle, dir, +- offset >> EXT3_BLOCK_SIZE_BITS(sb), 1, &retval); +- if (!bh) +- return retval; +- if (dir->i_size <= offset) { +- if (dir->i_size == 0) { +- brelse(bh); +- return -ENOENT; +- } ++ de->inode = 0; ++ de->rec_len = cpu_to_le16(rlen = blocksize); ++ nlen = 0; ++ return add_dirent_to_buf(handle, dentry, inode, de, bh); ++} + +- ext3_debug ("creating next block\n"); ++#ifdef CONFIG_EXT3_INDEX ++/* ++ * Returns 0 for success, or a negative error value ++ */ ++static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry, ++ struct inode *inode) ++{ ++ struct dx_frame frames[2], *frame; ++ struct dx_entry *entries, *at; ++ struct dx_hash_info hinfo; ++ struct buffer_head * bh; ++ struct inode *dir = dentry->d_parent->d_inode; ++ struct super_block * sb = dir->i_sb; ++ struct ext3_dir_entry_2 *de; ++ int err; + +- BUFFER_TRACE(bh, "get_write_access"); +- ext3_journal_get_write_access(handle, bh); +- de = (struct ext3_dir_entry_2 *) bh->b_data; +- de->inode = 0; +- de->rec_len = le16_to_cpu(sb->s_blocksize); +- dir->u.ext3_i.i_disksize = +- dir->i_size = offset + sb->s_blocksize; +- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; +- ext3_mark_inode_dirty(handle, dir); +- } else { ++ frame = dx_probe(dentry, 0, &hinfo, frames, &err); ++ if (!frame) ++ return err; ++ entries = frame->entries; ++ at = frame->at; + +- ext3_debug ("skipping to next block\n"); ++ if (!(bh = ext3_bread(handle,dir, dx_get_block(frame->at), 0, &err))) ++ goto cleanup; + +- de = (struct ext3_dir_entry_2 *) bh->b_data; +- } +- } +- if (!ext3_check_dir_entry ("ext3_add_entry", dir, de, bh, +- offset)) { +- brelse (bh); +- return -ENOENT; +- } +- if (ext3_match (namelen, name, de)) { +- brelse (bh); +- return -EEXIST; ++ BUFFER_TRACE(bh, "get_write_access"); ++ err = ext3_journal_get_write_access(handle, bh); ++ if (err) ++ goto journal_error; ++ ++ err = add_dirent_to_buf(handle, dentry, inode, 0, bh); ++ if (err != -ENOSPC) { ++ bh = 0; ++ goto cleanup; ++ } ++ ++ /* Block full, should compress but for now just split */ ++ dxtrace(printk("using %u of %u node entries\n", ++ dx_get_count(entries), dx_get_limit(entries))); ++ /* Need to split index? */ ++ if (dx_get_count(entries) == dx_get_limit(entries)) { ++ u32 newblock; ++ unsigned icount = dx_get_count(entries); ++ int levels = frame - frames; ++ struct dx_entry *entries2; ++ struct dx_node *node2; ++ struct buffer_head *bh2; ++ ++ if (levels && (dx_get_count(frames->entries) == ++ dx_get_limit(frames->entries))) { ++ ext3_warning(sb, __FUNCTION__, ++ "Directory index full!\n"); ++ err = -ENOSPC; ++ goto cleanup; + } +- if ((le32_to_cpu(de->inode) == 0 && +- le16_to_cpu(de->rec_len) >= rec_len) || +- (le16_to_cpu(de->rec_len) >= +- EXT3_DIR_REC_LEN(de->name_len) + rec_len)) { +- BUFFER_TRACE(bh, "get_write_access"); +- ext3_journal_get_write_access(handle, bh); +- /* By now the buffer is marked for journaling */ +- offset += le16_to_cpu(de->rec_len); +- if (le32_to_cpu(de->inode)) { +- de1 = (struct ext3_dir_entry_2 *) ((char *) de + +- EXT3_DIR_REC_LEN(de->name_len)); +- de1->rec_len = +- cpu_to_le16(le16_to_cpu(de->rec_len) - +- EXT3_DIR_REC_LEN(de->name_len)); +- de->rec_len = cpu_to_le16( +- EXT3_DIR_REC_LEN(de->name_len)); +- de = de1; ++ bh2 = ext3_append (handle, dir, &newblock, &err); ++ if (!(bh2)) ++ goto cleanup; ++ node2 = (struct dx_node *)(bh2->b_data); ++ entries2 = node2->entries; ++ node2->fake.rec_len = cpu_to_le16(sb->s_blocksize); ++ node2->fake.inode = 0; ++ BUFFER_TRACE(frame->bh, "get_write_access"); ++ err = ext3_journal_get_write_access(handle, frame->bh); ++ if (err) ++ goto journal_error; ++ if (levels) { ++ unsigned icount1 = icount/2, icount2 = icount - icount1; ++ unsigned hash2 = dx_get_hash(entries + icount1); ++ dxtrace(printk("Split index %i/%i\n", icount1, icount2)); ++ ++ BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */ ++ err = ext3_journal_get_write_access(handle, ++ frames[0].bh); ++ if (err) ++ goto journal_error; ++ ++ memcpy ((char *) entries2, (char *) (entries + icount1), ++ icount2 * sizeof(struct dx_entry)); ++ dx_set_count (entries, icount1); ++ dx_set_count (entries2, icount2); ++ dx_set_limit (entries2, dx_node_limit(dir)); ++ ++ /* Which index block gets the new entry? */ ++ if (at - entries >= icount1) { ++ frame->at = at = at - entries - icount1 + entries2; ++ frame->entries = entries = entries2; ++ swap(frame->bh, bh2); + } +- de->file_type = EXT3_FT_UNKNOWN; +- if (inode) { +- de->inode = cpu_to_le32(inode->i_ino); +- ext3_set_de_type(dir->i_sb, de, inode->i_mode); +- } else +- de->inode = 0; +- de->name_len = namelen; +- memcpy (de->name, name, namelen); +- /* +- * XXX shouldn't update any times until successful +- * completion of syscall, but too many callers depend +- * on this. +- * +- * XXX similarly, too many callers depend on +- * ext3_new_inode() setting the times, but error +- * recovery deletes the inode, so the worst that can +- * happen is that the times are slightly out of date +- * and/or different from the directory change time. +- */ +- dir->i_mtime = dir->i_ctime = CURRENT_TIME; +- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; +- dir->i_version = ++event; +- ext3_mark_inode_dirty(handle, dir); +- BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); +- ext3_journal_dirty_metadata(handle, bh); +- brelse(bh); +- return 0; ++ dx_insert_block (frames + 0, hash2, newblock); ++ dxtrace(dx_show_index ("node", frames[1].entries)); ++ dxtrace(dx_show_index ("node", ++ ((struct dx_node *) bh2->b_data)->entries)); ++ err = ext3_journal_dirty_metadata(handle, bh2); ++ if (err) ++ goto journal_error; ++ brelse (bh2); ++ } else { ++ dxtrace(printk("Creating second level index...\n")); ++ memcpy((char *) entries2, (char *) entries, ++ icount * sizeof(struct dx_entry)); ++ dx_set_limit(entries2, dx_node_limit(dir)); ++ ++ /* Set up root */ ++ dx_set_count(entries, 1); ++ dx_set_block(entries + 0, newblock); ++ ((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels = 1; ++ ++ /* Add new access path frame */ ++ frame = frames + 1; ++ frame->at = at = at - entries + entries2; ++ frame->entries = entries = entries2; ++ frame->bh = bh2; ++ err = ext3_journal_get_write_access(handle, ++ frame->bh); ++ if (err) ++ goto journal_error; + } +- offset += le16_to_cpu(de->rec_len); +- de = (struct ext3_dir_entry_2 *) +- ((char *) de + le16_to_cpu(de->rec_len)); ++ ext3_journal_dirty_metadata(handle, frames[0].bh); + } +- brelse (bh); +- return -ENOSPC; ++ de = do_split(handle, dir, &bh, frame, &hinfo, &err); ++ if (!de) ++ goto cleanup; ++ err = add_dirent_to_buf(handle, dentry, inode, de, bh); ++ bh = 0; ++ goto cleanup; ++ ++journal_error: ++ ext3_std_error(dir->i_sb, err); ++cleanup: ++ if (bh) ++ brelse(bh); ++ dx_release(frames); ++ return err; + } ++#endif + + /* + * ext3_delete_entry deletes a directory entry by merging it with the +@@ -454,9 +1546,11 @@ + struct inode * inode; + int err; + +- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -480,9 +1574,11 @@ + struct inode *inode; + int err; + +- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -508,9 +1604,11 @@ + if (dir->i_nlink >= EXT3_LINK_MAX) + return -EMLINK; + +- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 3); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -522,7 +1620,7 @@ + + inode->i_op = &ext3_dir_inode_operations; + inode->i_fop = &ext3_dir_operations; +- inode->i_size = inode->u.ext3_i.i_disksize = inode->i_sb->s_blocksize; ++ inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize; + inode->i_blocks = 0; + dir_block = ext3_bread (handle, inode, 0, 1, &err); + if (!dir_block) { +@@ -555,21 +1653,19 @@ + inode->i_mode |= S_ISGID; + ext3_mark_inode_dirty(handle, inode); + err = ext3_add_entry (handle, dentry, inode); +- if (err) +- goto out_no_entry; ++ if (err) { ++ inode->i_nlink = 0; ++ ext3_mark_inode_dirty(handle, inode); ++ iput (inode); ++ goto out_stop; ++ } + dir->i_nlink++; +- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; ++ ext3_update_dx_flag(dir); + ext3_mark_inode_dirty(handle, dir); + d_instantiate(dentry, inode); + out_stop: + ext3_journal_stop(handle, dir); + return err; +- +-out_no_entry: +- inode->i_nlink = 0; +- ext3_mark_inode_dirty(handle, inode); +- iput (inode); +- goto out_stop; + } + + /* +@@ -656,7 +1752,7 @@ + int err = 0, rc; + + lock_super(sb); +- if (!list_empty(&inode->u.ext3_i.i_orphan)) ++ if (!list_empty(&EXT3_I(inode)->i_orphan)) + goto out_unlock; + + /* Orphan handling is only valid for files with data blocks +@@ -697,7 +1793,7 @@ + * This is safe: on error we're going to ignore the orphan list + * anyway on the next recovery. */ + if (!err) +- list_add(&inode->u.ext3_i.i_orphan, &EXT3_SB(sb)->s_orphan); ++ list_add(&EXT3_I(inode)->i_orphan, &EXT3_SB(sb)->s_orphan); + + jbd_debug(4, "superblock will point to %ld\n", inode->i_ino); + jbd_debug(4, "orphan inode %ld will point to %d\n", +@@ -715,25 +1811,26 @@ + int ext3_orphan_del(handle_t *handle, struct inode *inode) + { + struct list_head *prev; ++ struct ext3_inode_info *ei = EXT3_I(inode); + struct ext3_sb_info *sbi; + unsigned long ino_next; + struct ext3_iloc iloc; + int err = 0; + + lock_super(inode->i_sb); +- if (list_empty(&inode->u.ext3_i.i_orphan)) { ++ if (list_empty(&ei->i_orphan)) { + unlock_super(inode->i_sb); + return 0; + } + + ino_next = NEXT_ORPHAN(inode); +- prev = inode->u.ext3_i.i_orphan.prev; ++ prev = ei->i_orphan.prev; + sbi = EXT3_SB(inode->i_sb); + + jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino); + +- list_del(&inode->u.ext3_i.i_orphan); +- INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan); ++ list_del(&ei->i_orphan); ++ INIT_LIST_HEAD(&ei->i_orphan); + + /* If we're on an error path, we may not have a valid + * transaction handle with which to update the orphan list on +@@ -794,8 +1891,9 @@ + handle_t *handle; + + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); +- if (IS_ERR(handle)) ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + retval = -ENOENT; + bh = ext3_find_entry (dentry, &de); +@@ -833,7 +1931,7 @@ + dir->i_nlink--; + inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; + ext3_mark_inode_dirty(handle, inode); +- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; ++ ext3_update_dx_flag(dir); + ext3_mark_inode_dirty(handle, dir); + + end_rmdir: +@@ -851,8 +1949,9 @@ + handle_t *handle; + + handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS); +- if (IS_ERR(handle)) ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -879,7 +1978,7 @@ + if (retval) + goto end_unlink; + dir->i_ctime = dir->i_mtime = CURRENT_TIME; +- dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; ++ ext3_update_dx_flag(dir); + ext3_mark_inode_dirty(handle, dir); + inode->i_nlink--; + if (!inode->i_nlink) +@@ -905,9 +2004,11 @@ + if (l > dir->i_sb->s_blocksize) + return -ENAMETOOLONG; + +- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + 5); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -917,7 +2018,7 @@ + if (IS_ERR(inode)) + goto out_stop; + +- if (l > sizeof (inode->u.ext3_i.i_data)) { ++ if (l > sizeof (EXT3_I(inode)->i_data)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_mapping->a_ops = &ext3_aops; + /* +@@ -926,24 +2027,22 @@ + * i_size in generic_commit_write(). + */ + err = block_symlink(inode, symname, l); +- if (err) +- goto out_no_entry; ++ if (err) { ++ ext3_dec_count(handle, inode); ++ ext3_mark_inode_dirty(handle, inode); ++ iput (inode); ++ goto out_stop; ++ } + } else { + inode->i_op = &ext3_fast_symlink_inode_operations; +- memcpy((char*)&inode->u.ext3_i.i_data,symname,l); ++ memcpy((char*)&EXT3_I(inode)->i_data,symname,l); + inode->i_size = l-1; + } +- inode->u.ext3_i.i_disksize = inode->i_size; ++ EXT3_I(inode)->i_disksize = inode->i_size; + err = ext3_add_nondir(handle, dentry, inode); + out_stop: + ext3_journal_stop(handle, dir); + return err; +- +-out_no_entry: +- ext3_dec_count(handle, inode); +- ext3_mark_inode_dirty(handle, inode); +- iput (inode); +- goto out_stop; + } + + static int ext3_link (struct dentry * old_dentry, +@@ -956,12 +2055,15 @@ + if (S_ISDIR(inode->i_mode)) + return -EPERM; + +- if (inode->i_nlink >= EXT3_LINK_MAX) ++ if (inode->i_nlink >= EXT3_LINK_MAX) { + return -EMLINK; ++ } + +- handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(dir)) + handle->h_sync = 1; +@@ -994,9 +2096,11 @@ + + old_bh = new_bh = dir_bh = NULL; + +- handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + 2); +- if (IS_ERR(handle)) ++ handle = ext3_journal_start(old_dir, 2 * EXT3_DATA_TRANS_BLOCKS + ++ EXT3_INDEX_EXTRA_TRANS_BLOCKS + 2); ++ if (IS_ERR(handle)) { + return PTR_ERR(handle); ++ } + + if (IS_SYNC(old_dir) || IS_SYNC(new_dir)) + handle->h_sync = 1; +@@ -1069,14 +2173,37 @@ + /* + * ok, that's it + */ +- ext3_delete_entry(handle, old_dir, old_de, old_bh); ++ if (le32_to_cpu(old_de->inode) != old_inode->i_ino || ++ old_de->name_len != old_dentry->d_name.len || ++ strncmp(old_de->name, old_dentry->d_name.name, old_de->name_len) || ++ (retval = ext3_delete_entry(handle, old_dir, ++ old_de, old_bh)) == -ENOENT) { ++ /* old_de could have moved from under us during htree split, so ++ * make sure that we are deleting the right entry. We might ++ * also be pointing to a stale entry in the unused part of ++ * old_bh so just checking inum and the name isn't enough. */ ++ struct buffer_head *old_bh2; ++ struct ext3_dir_entry_2 *old_de2; ++ ++ old_bh2 = ext3_find_entry(old_dentry, &old_de2); ++ if (old_bh2) { ++ retval = ext3_delete_entry(handle, old_dir, ++ old_de2, old_bh2); ++ brelse(old_bh2); ++ } ++ } ++ if (retval) { ++ ext3_warning(old_dir->i_sb, "ext3_rename", ++ "Deleting old file (%lu), %d, error=%d", ++ old_dir->i_ino, old_dir->i_nlink, retval); ++ } + + if (new_inode) { + new_inode->i_nlink--; + new_inode->i_ctime = CURRENT_TIME; + } + old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; +- old_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; ++ ext3_update_dx_flag(old_dir); + if (dir_bh) { + BUFFER_TRACE(dir_bh, "get_write_access"); + ext3_journal_get_write_access(handle, dir_bh); +@@ -1088,7 +2215,7 @@ + new_inode->i_nlink--; + } else { + new_dir->i_nlink++; +- new_dir->u.ext3_i.i_flags &= ~EXT3_INDEX_FL; ++ ext3_update_dx_flag(new_dir); + ext3_mark_inode_dirty(handle, new_dir); + } + } +Index: linux-2.4.29/fs/ext3/super.c +=================================================================== +--- linux-2.4.29.orig/fs/ext3/super.c 2005-04-07 18:59:19.000000000 +0300 ++++ linux-2.4.29/fs/ext3/super.c 2005-05-03 16:29:50.580498544 +0300 +@@ -712,6 +712,7 @@ + es->s_mtime = cpu_to_le32(CURRENT_TIME); + ext3_update_dynamic_rev(sb); + EXT3_SET_INCOMPAT_FEATURE(sb, EXT3_FEATURE_INCOMPAT_RECOVER); ++ + ext3_commit_super (sb, es, 1); + if (test_opt (sb, DEBUG)) + printk (KERN_INFO +@@ -722,6 +723,7 @@ + EXT3_BLOCKS_PER_GROUP(sb), + EXT3_INODES_PER_GROUP(sb), + sbi->s_mount_opt); ++ + printk(KERN_INFO "EXT3 FS " EXT3FS_VERSION ", " EXT3FS_DATE " on %s, ", + bdevname(sb->s_dev)); + if (EXT3_SB(sb)->s_journal->j_inode == NULL) { +@@ -915,6 +917,7 @@ + return res; + } + ++ + struct super_block * ext3_read_super (struct super_block * sb, void * data, + int silent) + { +@@ -1094,6 +1097,9 @@ + sbi->s_mount_state = le16_to_cpu(es->s_state); + sbi->s_addr_per_block_bits = log2(EXT3_ADDR_PER_BLOCK(sb)); + sbi->s_desc_per_block_bits = log2(EXT3_DESC_PER_BLOCK(sb)); ++ for (i=0; i < 4; i++) ++ sbi->s_hash_seed[i] = le32_to_cpu(es->s_hash_seed[i]); ++ sbi->s_def_hash_version = es->s_def_hash_version; + + if (sbi->s_blocks_per_group > blocksize * 8) { + printk (KERN_ERR +@@ -1845,6 +1851,7 @@ + unregister_filesystem(&ext3_fs_type); + } + ++EXPORT_SYMBOL(ext3_force_commit); + EXPORT_SYMBOL(ext3_bread); + + MODULE_AUTHOR("Remy Card, Stephen Tweedie, Andrew Morton, Andreas Dilger, Theodore Ts'o and others"); +Index: linux-2.4.29/include/linux/ext3_fs.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs.h 2005-04-07 18:52:26.000000000 +0300 ++++ linux-2.4.29/include/linux/ext3_fs.h 2005-05-03 16:29:50.584497936 +0300 +@@ -40,6 +40,11 @@ + #define EXT3FS_VERSION "2.4-0.9.19" + + /* ++ * Always enable hashed directories ++ */ ++#define CONFIG_EXT3_INDEX ++ ++/* + * Debug code + */ + #ifdef EXT3FS_DEBUG +@@ -593,9 +598,46 @@ + #define EXT3_DIR_ROUND (EXT3_DIR_PAD - 1) + #define EXT3_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT3_DIR_ROUND) & \ + ~EXT3_DIR_ROUND) ++/* ++ * Hash Tree Directory indexing ++ * (c) Daniel Phillips, 2001 ++ */ ++ ++#ifdef CONFIG_EXT3_INDEX ++ #define is_dx(dir) (EXT3_HAS_COMPAT_FEATURE(dir->i_sb, \ ++ EXT3_FEATURE_COMPAT_DIR_INDEX) && \ ++ (EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) ++#define EXT3_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT3_LINK_MAX) ++#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1) ++#else ++ #define is_dx(dir) 0 ++#define EXT3_DIR_LINK_MAX(dir) ((dir)->i_nlink >= EXT3_LINK_MAX) ++#define EXT3_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2) ++#endif ++ ++/* Legal values for the dx_root hash_version field: */ ++ ++#define DX_HASH_LEGACY 0 ++#define DX_HASH_HALF_MD4 1 ++#define DX_HASH_TEA 2 ++ ++/* hash info structure used by the directory hash */ ++struct dx_hash_info ++{ ++ u32 hash; ++ u32 minor_hash; ++ int hash_version; ++ u32 *seed; ++}; + + #ifdef __KERNEL__ + /* ++ * Control parameters used by ext3_htree_next_block ++ */ ++#define HASH_NB_ALWAYS 1 ++ ++ ++/* + * Describe an inode's exact location on disk and in memory + */ + struct ext3_iloc +@@ -605,6 +647,27 @@ + unsigned long block_group; + }; + ++ ++/* ++ * This structure is stuffed into the struct file's private_data field ++ * for directories. It is where we put information so that we can do ++ * readdir operations in hash tree order. ++ */ ++struct dir_private_info { ++ rb_root_t root; ++ rb_node_t *curr_node; ++ struct fname *extra_fname; ++ loff_t last_pos; ++ __u32 curr_hash; ++ __u32 curr_minor_hash; ++ __u32 next_hash; ++}; ++ ++/* ++ * Special error return code only used by dx_probe() and its callers. ++ */ ++#define ERR_BAD_DX_DIR -75000 ++ + /* + * Function prototypes + */ +@@ -632,11 +695,20 @@ + + /* dir.c */ + extern int ext3_check_dir_entry(const char *, struct inode *, +- struct ext3_dir_entry_2 *, struct buffer_head *, +- unsigned long); ++ struct ext3_dir_entry_2 *, ++ struct buffer_head *, unsigned long); ++extern int ext3_htree_store_dirent(struct file *dir_file, __u32 hash, ++ __u32 minor_hash, ++ struct ext3_dir_entry_2 *dirent); ++extern void ext3_htree_free_dir_info(struct dir_private_info *p); ++ + /* fsync.c */ + extern int ext3_sync_file (struct file *, struct dentry *, int); + ++/* hash.c */ ++extern int ext3fs_dirhash(const char *name, int len, struct ++ dx_hash_info *hinfo); ++ + /* ialloc.c */ + extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int); + extern void ext3_free_inode (handle_t *, struct inode *); +@@ -669,6 +741,8 @@ + /* namei.c */ + extern int ext3_orphan_add(handle_t *, struct inode *); + extern int ext3_orphan_del(handle_t *, struct inode *); ++extern int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash, ++ __u32 start_minor_hash, __u32 *next_hash); + + /* super.c */ + extern void ext3_error (struct super_block *, const char *, const char *, ...) +Index: linux-2.4.29/include/linux/ext3_fs_sb.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_fs_sb.h 2005-04-07 18:54:55.000000000 +0300 ++++ linux-2.4.29/include/linux/ext3_fs_sb.h 2005-05-03 16:29:50.586497632 +0300 +@@ -62,6 +62,8 @@ + int s_inode_size; + int s_first_ino; + u32 s_next_generation; ++ u32 s_hash_seed[4]; ++ int s_def_hash_version; + + /* Journaling */ + struct inode * s_journal_inode; +Index: linux-2.4.29/include/linux/ext3_jbd.h +=================================================================== +--- linux-2.4.29.orig/include/linux/ext3_jbd.h 2005-04-07 18:52:32.000000000 +0300 ++++ linux-2.4.29/include/linux/ext3_jbd.h 2005-05-03 16:29:50.587497480 +0300 +@@ -63,6 +63,8 @@ + + #define EXT3_RESERVE_TRANS_BLOCKS 12U + ++#define EXT3_INDEX_EXTRA_TRANS_BLOCKS 8 ++ + int + ext3_mark_iloc_dirty(handle_t *handle, + struct inode *inode, diff --git a/lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch new file mode 100644 index 0000000..e8ed192 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.5-suse.patch @@ -0,0 +1,23 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c 2005-04-04 05:09:18.000000000 -0600 +@@ -926,8 +926,16 @@ + struct inode *dir = dentry->d_parent->d_inode; + + sb = dir->i_sb; +- if (!(frame = dx_probe (dentry, 0, &hinfo, frames, err))) +- return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); diff --git a/lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch new file mode 100644 index 0000000..9192112 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-htree-dot-2.6.patch @@ -0,0 +1,23 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/namei.c 2005-04-04 05:06:46.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/namei.c 2005-04-04 05:09:18.000000000 -0600 +@@ -926,8 +926,16 @@ + struct inode *dir = dentry->d_parent->d_inode; + + sb = dir->i_sb; +- if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) +- return NULL; ++ /* NFS may look up ".." - look at dx_root directory block */ ++ if (namelen > 2 || name[0] != '.'||(name[1] != '.' && name[1] != '\0')){ ++ if (!(frame = dx_probe(dentry, NULL, &hinfo, frames, err))) ++ return NULL; ++ } else { ++ frame = frames; ++ frame->bh = NULL; /* for dx_release() */ ++ frame->at = (struct dx_entry *)frames; /* hack for zero entry*/ ++ dx_set_block(frame->at, 0); /* dx_root block is 0 */ ++ } + hash = hinfo.hash; + do { + block = dx_get_block(frame->at); diff --git a/lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch b/lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch new file mode 100644 index 0000000..83e25fa --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-ialloc-2.4.24.patch @@ -0,0 +1,238 @@ +Index: lum/fs/ext3/ialloc.c +=================================================================== +--- lum.orig/fs/ext3/ialloc.c 2004-08-26 13:14:35.000000000 -0600 ++++ lum/fs/ext3/ialloc.c 2004-08-31 15:00:35.000000000 -0600 +@@ -327,8 +327,130 @@ int ext3_itable_block_used(struct super_ + * directories already is chosen. + * + * For other inodes, search forward from the parent directory's block +- * group to find a free inode. ++ * group to find a free inode in a group with some free blocks. + */ ++static int find_group_dir(struct super_block *sb, const struct inode *parent, ++ struct ext3_group_desc **best_desc, ++ struct buffer_head **best_bh) ++{ ++ struct ext3_sb_info *sbi = EXT3_SB(sb); ++ int ngroups = sbi->s_groups_count; ++ int avefreei; ++ struct ext3_group_desc *desc; ++ struct buffer_head *bh; ++ int group, best_group = -1, ndir_best = 999999999; ++ ++ *best_desc = NULL; ++ *best_bh = NULL; ++ ++ avefreei = le32_to_cpu(sbi->s_es->s_free_inodes_count) / ngroups; ++ ++ for (group = 0; group < ngroups; group++) { ++ desc = ext3_get_group_desc(sb, group, &bh); ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ if (le16_to_cpu(desc->bg_free_inodes_count) < avefreei) ++ continue; ++ if (le16_to_cpu(desc->bg_used_dirs_count) > ndir_best) ++ continue; ++ if (!*best_desc || ++ (le16_to_cpu(desc->bg_free_blocks_count) > ++ le16_to_cpu((*best_desc)->bg_free_blocks_count))) { ++ *best_bh = bh; ++ *best_desc = desc; ++ best_group = group; ++ ndir_best = le16_to_cpu(desc->bg_used_dirs_count); ++ } ++ } ++ ++ return best_group; ++} ++ ++static int find_group_other(struct super_block *sb, const struct inode *parent, ++ struct ext3_group_desc **best_desc, ++ struct buffer_head **best_bh) ++{ ++ struct ext3_sb_info *sbi = EXT3_SB(sb); ++ int parent_group = EXT3_I(parent)->i_block_group; ++ int ngroups = sbi->s_groups_count; ++ int avefreeb; ++ struct ext3_group_desc *desc; ++ struct buffer_head *bh; ++ int group, i, best_group = -1; ++ ++ *best_desc = NULL; ++ *best_bh = NULL; ++ ++ /* ++ * Try to place the inode in its parent directory ++ */ ++ group = parent_group; ++ desc = ext3_get_group_desc (sb, group, &bh); ++ if (desc && le16_to_cpu(desc->bg_free_inodes_count) && ++ le16_to_cpu(desc->bg_free_blocks_count)) { ++ *best_desc = desc; ++ *best_bh = bh; ++ return group; ++ } ++ ++ /* ++ * We're going to place this inode in a different blockgroup from its ++ * parent. We want to cause files in a common directory to all land in ++ * the same blockgroup if it has space. But we want files which are ++ * in a different directory which shares a blockgroup with our parent ++ * to land in a different blockgroup. ++ * ++ * So add our directory's i_ino into the starting point for the hash. ++ */ ++ group = (group + parent->i_ino) % ngroups; ++ ++ avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) / ++ sbi->s_groups_count / ngroups; ++ ++ /* ++ * Use a quadratic hash to find a group with a free inode and some free ++ * blocks. ++ */ ++ for (i = 1; i < ngroups; i <<= 1) { ++ group += i; ++ if (group >= ngroups) ++ group -= ngroups; ++ desc = ext3_get_group_desc(sb, group, &bh); ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ if (le16_to_cpu(desc->bg_free_blocks_count) > avefreeb) { ++ *best_bh = bh; ++ *best_desc = desc; ++ return group; ++ } ++ } ++ ++ /* ++ * That failed: try linear search for a group with free inodes and ++ * preferrably free blocks, returning as soon as we find a good one. ++ */ ++ group = sbi->s_last_group; ++ for (i = 0; i < ngroups; i++) { ++ if (++group >= ngroups) ++ group = 0; ++ desc = ext3_get_group_desc(sb, group, &bh); ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ if (!*best_desc || ++ (le16_to_cpu(desc->bg_free_blocks_count) > ++ le16_to_cpu((*best_desc)->bg_free_blocks_count))) { ++ *best_bh = bh; ++ *best_desc = desc; ++ best_group = group; ++ if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb) ++ break; ++ } ++ } ++ sbi->s_last_group = best_group; ++ ++ return best_group; ++} ++ + struct inode * ext3_new_inode (handle_t *handle, + const struct inode * dir, int mode, + unsigned long goal) +@@ -336,11 +459,10 @@ struct inode * ext3_new_inode (handle_t + struct super_block * sb; + struct buffer_head * bh; + struct buffer_head * bh2; +- int i, j, avefreei; ++ int i, j; + struct inode * inode; + int bitmap_nr; + struct ext3_group_desc * gdp; +- struct ext3_group_desc * tmp; + struct ext3_super_block * es; + struct ext3_iloc iloc; + int err = 0; +@@ -392,72 +514,10 @@ struct inode * ext3_new_inode (handle_t + } + + repeat: +- gdp = NULL; +- i = 0; +- +- if (S_ISDIR(mode)) { +- avefreei = le32_to_cpu(es->s_free_inodes_count) / +- sb->u.ext3_sb.s_groups_count; +- if (!gdp) { +- for (j = 0; j < sb->u.ext3_sb.s_groups_count; j++) { +- struct buffer_head *temp_buffer; +- tmp = ext3_get_group_desc (sb, j, &temp_buffer); +- if (tmp && +- le16_to_cpu(tmp->bg_free_inodes_count) && +- le16_to_cpu(tmp->bg_free_inodes_count) >= +- avefreei) { +- if (!gdp || (le16_to_cpu(tmp->bg_free_blocks_count) > +- le16_to_cpu(gdp->bg_free_blocks_count))) { +- i = j; +- gdp = tmp; +- bh2 = temp_buffer; +- } +- } +- } +- } +- } else { +- /* +- * Try to place the inode in its parent directory +- */ +- i = dir->u.ext3_i.i_block_group; +- tmp = ext3_get_group_desc (sb, i, &bh2); +- if (tmp && le16_to_cpu(tmp->bg_free_inodes_count)) +- gdp = tmp; +- else +- { +- /* +- * Use a quadratic hash to find a group with a +- * free inode +- */ +- for (j = 1; j < sb->u.ext3_sb.s_groups_count; j <<= 1) { +- i += j; +- if (i >= sb->u.ext3_sb.s_groups_count) +- i -= sb->u.ext3_sb.s_groups_count; +- tmp = ext3_get_group_desc (sb, i, &bh2); +- if (tmp && +- le16_to_cpu(tmp->bg_free_inodes_count)) { +- gdp = tmp; +- break; +- } +- } +- } +- if (!gdp) { +- /* +- * That failed: try linear search for a free inode +- */ +- i = dir->u.ext3_i.i_block_group + 1; +- for (j = 2; j < sb->u.ext3_sb.s_groups_count; j++) { +- if (++i >= sb->u.ext3_sb.s_groups_count) +- i = 0; +- tmp = ext3_get_group_desc (sb, i, &bh2); +- if (tmp && +- le16_to_cpu(tmp->bg_free_inodes_count)) { +- gdp = tmp; +- break; +- } +- } +- } +- } ++ if (S_ISDIR(mode)) ++ i = find_group_dir(sb, dir, &gdp, &bh2); ++ else ++ i = find_group_other(sb, dir, &gdp, &bh2); + + err = -ENOSPC; + if (!gdp) +Index: lum/include/linux/ext3_fs_sb.h +=================================================================== +--- lum.orig/include/linux/ext3_fs_sb.h 2004-08-26 13:28:53.000000000 -0600 ++++ lum/include/linux/ext3_fs_sb.h 2004-08-31 11:04:27.000000000 -0600 +@@ -45,6 +45,7 @@ struct ext3_sb_info { + unsigned long s_gdb_count; /* Number of group descriptor blocks */ + unsigned long s_desc_per_block; /* Number of group descriptors per block */ + unsigned long s_groups_count; /* Number of groups in the fs */ ++ unsigned long s_last_group; /* Last group used for inode allocation */ + struct buffer_head * s_sbh; /* Buffer containing the super block */ + struct ext3_super_block * s_es; /* Pointer to the super block in the buffer */ + struct buffer_head ** s_group_desc; diff --git a/lustre/kernel_patches/patches/ext3-ialloc-2.6.patch b/lustre/kernel_patches/patches/ext3-ialloc-2.6.patch new file mode 100644 index 0000000..15d37a9 --- /dev/null +++ b/lustre/kernel_patches/patches/ext3-ialloc-2.6.patch @@ -0,0 +1,128 @@ +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/ialloc.c 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/ialloc.c 2005-05-16 14:18:29.000000000 -0600 +@@ -352,13 +352,17 @@ + return -1; + } + +-static int find_group_other(struct super_block *sb, struct inode *parent) ++static int find_group_other(struct super_block *sb, struct inode *parent, ++ int mode) + { + int parent_group = EXT3_I(parent)->i_block_group; ++ struct ext3_sb_info *sbi = EXT3_SB(sb); + int ngroups = EXT3_SB(sb)->s_groups_count; + struct ext3_group_desc *desc; + struct buffer_head *bh; + int group, i; ++ int best_group = -1; ++ int avefreeb, freeb, best_group_freeb = 0; + + /* + * Try to place the inode in its parent directory +@@ -366,9 +370,9 @@ + group = parent_group; + desc = ext3_get_group_desc (sb, group, &bh); + if (desc && le16_to_cpu(desc->bg_free_inodes_count) && +- le16_to_cpu(desc->bg_free_blocks_count)) ++ (!S_ISREG(mode) || le16_to_cpu(desc->bg_free_blocks_count))) + return group; +- ++ avefreeb = le32_to_cpu(sbi->s_es->s_free_blocks_count) / ngroups; + /* + * We're going to place this inode in a different blockgroup from its + * parent. We want to cause files in a common directory to all land in +@@ -381,33 +385,47 @@ + group = (group + parent->i_ino) % ngroups; + + /* +- * Use a quadratic hash to find a group with a free inode and some free +- * blocks. ++ * Use a quadratic hash to find a group with a free inode and ++ * average number of free blocks. + */ + for (i = 1; i < ngroups; i <<= 1) { + group += i; + if (group >= ngroups) + group -= ngroups; + desc = ext3_get_group_desc (sb, group, &bh); +- if (desc && le16_to_cpu(desc->bg_free_inodes_count) && +- le16_to_cpu(desc->bg_free_blocks_count)) ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ if (!S_ISREG(mode)) ++ return group; ++ if (le16_to_cpu(desc->bg_free_blocks_count) >= avefreeb) + return group; + } + + /* +- * That failed: try linear search for a free inode, even if that group +- * has no free blocks. ++ * That failed: start from last group used to allocate inode ++ * try linear search for a free inode and prefereably ++ * free blocks. + */ +- group = parent_group; ++ group = sbi->s_last_alloc_group; ++ if (group == -1) ++ group = parent_group; ++ + for (i = 0; i < ngroups; i++) { + if (++group >= ngroups) + group = 0; + desc = ext3_get_group_desc (sb, group, &bh); +- if (desc && le16_to_cpu(desc->bg_free_inodes_count)) +- return group; ++ if (!desc || !desc->bg_free_inodes_count) ++ continue; ++ freeb = le16_to_cpu(desc->bg_free_blocks_count); ++ if (freeb > best_group_freeb) { ++ best_group_freeb = freeb; ++ best_group = group; ++ if (freeb >= avefreeb || !S_ISREG(mode)) ++ break; ++ } + } +- +- return -1; ++ sbi->s_last_alloc_group = best_group; ++ return best_group; + } + + /* +@@ -454,7 +472,7 @@ + else + group = find_group_orlov(sb, dir); + } else +- group = find_group_other(sb, dir); ++ group = find_group_other(sb, dir, mode); + + err = -ENOSPC; + if (group == -1) +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/fs/ext3/super.c 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/fs/ext3/super.c 2005-05-16 14:17:14.000000000 -0600 +@@ -1297,6 +1297,7 @@ + percpu_counter_init(&sbi->s_dirs_counter); + bgl_lock_init(&sbi->s_blockgroup_lock); + ++ sbi->s_last_alloc_group = -1; + for (i = 0; i < db_count; i++) { + block = descriptor_loc(sb, logic_sb_block, i); + sbi->s_group_desc[i] = sb_bread(sb, block); +Index: linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h +=================================================================== +--- linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891.orig/include/linux/ext3_fs_sb.h 2005-05-16 14:10:54.000000000 -0600 ++++ linux-2.6.5-SLES9_SP1_BRANCH_2004111114454891/include/linux/ext3_fs_sb.h 2005-05-16 14:17:14.000000000 -0600 +@@ -59,6 +59,8 @@ + struct percpu_counter s_freeinodes_counter; + struct percpu_counter s_dirs_counter; + struct blockgroup_lock s_blockgroup_lock; ++ /* Last group used to allocate inode */ ++ int s_last_alloc_group; + + /* root of the per fs reservation window tree */ + spinlock_t s_rsv_window_lock; diff --git a/lustre/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch b/lustre/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch index 4de2b32..d38fb54 100644 --- a/lustre/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch +++ b/lustre/kernel_patches/patches/ext3-mballoc2-2.6-suse.patch @@ -2,7 +2,7 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c =================================================================== --- linux-2.6.5-suse.orig/fs/ext3/mballoc.c 2005-03-02 22:42:20.659360368 +0300 +++ linux-2.6.5-suse/fs/ext3/mballoc.c 2005-03-11 16:13:13.000000000 +0300 -@@ -0,0 +1,1863 @@ +@@ -0,0 +1,1864 @@ +/* + * Copyright(c) 2003, 2004, 2005, Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas @@ -932,8 +932,9 @@ Index: linux-2.6.5-suse/fs/ext3/mballoc.c + * We've been searching too long. Let's try to allocate + * the best chunk we've found so far + */ -+ printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n", -+ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); ++ ext3_warning(inode->i_sb, __FUNCTION__, ++ "too long searching: got %d want %d\n", ++ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); + ext3_mb_try_best_found(&ac, &e3b); + if (ac.ac_status != AC_STATUS_FOUND) { + /* diff --git a/lustre/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch b/lustre/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch index dc6ffe3..24b2595 100644 --- a/lustre/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch +++ b/lustre/kernel_patches/patches/ext3-mballoc2-2.6.9-rhel4.patch @@ -2,7 +2,7 @@ Index: linux-stage/fs/ext3/mballoc.c =================================================================== --- linux-stage.orig/fs/ext3/mballoc.c 2005-02-25 17:28:41.836311072 +0200 +++ linux-stage/fs/ext3/mballoc.c 2005-02-25 17:28:41.859307576 +0200 -@@ -0,0 +1,1860 @@ +@@ -0,0 +1,1861 @@ +/* + * Copyright (c) 2003, Cluster File Systems, Inc, info@clusterfs.com + * Written by Alex Tomas @@ -932,8 +932,9 @@ Index: linux-stage/fs/ext3/mballoc.c + * We've been searching too long. Let's try to allocate + * the best chunk we've found so far + */ -+ printk(KERN_ERR "EXT3-fs: too long searching (%d/%d)\n", -+ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); ++ ext3_warning(inode->i_sb, __FUNCTION__, ++ "too long searching: got %d want %d\n", ++ ac.ac_b_ex.fe_len, ac.ac_g_ex.fe_len); + ext3_mb_try_best_found(&ac, &e3b); + if (ac.ac_status != AC_STATUS_FOUND) { + /* diff --git a/lustre/kernel_patches/patches/fsprivate-2.4-suse.patch b/lustre/kernel_patches/patches/fsprivate-2.4-suse.patch new file mode 100644 index 0000000..fc3a72a --- /dev/null +++ b/lustre/kernel_patches/patches/fsprivate-2.4-suse.patch @@ -0,0 +1,10 @@ +--- uml-2.4.24/include/linux/fs.h.orig 2005-05-18 21:27:58.120742112 +0300 ++++ uml-2.4.24/include/linux/fs.h 2005-05-18 22:48:12.081908776 +0300 +@@ -592,6 +592,7 @@ + /* needed for tty driver, and maybe others */ + void *private_data; + struct lookup_intent *f_it; ++ void *fs_private; + struct list_head f_ep_links; + spinlock_t f_ep_lock; + }; diff --git a/lustre/kernel_patches/patches/fsprivate-2.4.patch b/lustre/kernel_patches/patches/fsprivate-2.4.patch new file mode 100644 index 0000000..4e83dbe --- /dev/null +++ b/lustre/kernel_patches/patches/fsprivate-2.4.patch @@ -0,0 +1,10 @@ +--- uml-2.4.24/include/linux/fs.h.orig 2005-05-18 21:27:58.120742112 +0300 ++++ uml-2.4.24/include/linux/fs.h 2005-05-18 22:48:12.081908776 +0300 +@@ -592,6 +592,7 @@ + /* needed for tty driver, and maybe others */ + void *private_data; + struct lookup_intent *f_it; ++ void *fs_private; + + /* preallocated helper kiobuf to speedup O_DIRECT */ + struct kiobuf *f_iobuf; diff --git a/lustre/kernel_patches/patches/fsprivate-2.6.patch b/lustre/kernel_patches/patches/fsprivate-2.6.patch new file mode 100644 index 0000000..a5c3d48 --- /dev/null +++ b/lustre/kernel_patches/patches/fsprivate-2.6.patch @@ -0,0 +1,10 @@ +--- linux-2.6.5-7.141/include/linux/fs.h.orig 2005-02-01 23:56:07.000000000 +0200 ++++ linux-2.6.5-7.141/include/linux/fs.h 2005-05-18 23:23:15.486142728 +0300 +@@ -574,6 +574,7 @@ + #endif /* #ifdef CONFIG_EPOLL */ + struct address_space *f_mapping; + struct lookup_intent *f_it; ++ void *fs_private; + }; + extern spinlock_t files_lock; + #define file_list_lock() spin_lock(&files_lock); diff --git a/lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch b/lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch new file mode 100644 index 0000000..6686b15 --- /dev/null +++ b/lustre/kernel_patches/patches/gfp_debug-2.4.21-rhel.patch @@ -0,0 +1,77 @@ +Index: linux-2.4.21-rhel/kernel/sysctl.c +=================================================================== +--- linux-2.4.21-rhel.orig/kernel/sysctl.c 2005-01-20 18:52:39.000000000 -0700 ++++ linux-2.4.21-rhel/kernel/sysctl.c 2005-05-31 18:47:22.000000000 -0600 +@@ -355,6 +355,8 @@ + extern int skip_mapped_pages; + + static ctl_table vm_table[] = { ++ {VM_GFP_DEBUG, "vm_gfp_debug", ++ &vm_gfp_debug, sizeof(int), 0644, NULL, &proc_dointvec}, + {VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, NULL, + &bdflush_min, &bdflush_max}, +Index: linux-2.4.21-rhel/mm/page_alloc.c +=================================================================== +--- linux-2.4.21-rhel.orig/mm/page_alloc.c 2005-05-31 18:29:37.000000000 -0600 ++++ linux-2.4.21-rhel/mm/page_alloc.c 2005-05-31 18:49:27.000000000 -0600 +@@ -58,6 +58,8 @@ + static int zone_extrafree_max[MAX_NR_ZONES] __initdata = { 1024 , 1024, 0, }; + #endif + ++int vm_gfp_debug = 0; ++ + /* + * Temporary debugging check. + */ +@@ -773,8 +775,12 @@ + } + + out_failed: +- /* No luck.. */ +-// printk(KERN_ERR "__alloc_pages: %lu-order allocation failed.\n", order); ++ if (!(gfp_mask & __GFP_NOWARN)) { ++ printk(KERN_ERR "__alloc_pages: %lu-order allocation failed (gfp=%#x/%i).\n", ++ order, gfp_mask, !!(current->flags & PF_MEMALLOC)); ++ if (unlikely(vm_gfp_debug)) ++ dump_stack(); ++ } + return NULL; + } + +Index: linux-2.4.21-rhel/include/linux/sysctl.h +=================================================================== +--- linux-2.4.21-rhel.orig/include/linux/sysctl.h 2005-03-18 18:25:26.000000000 -0700 ++++ linux-2.4.21-rhel/include/linux/sysctl.h 2005-05-31 18:48:17.000000000 -0600 +@@ -153,6 +153,7 @@ + VM_MIN_READAHEAD=12, /* Min file readahead */ + VM_MAX_READAHEAD=13, /* Max file readahead */ + VM_OVERCOMMIT_RATIO=16, /* percent of RAM to allow overcommit in */ ++ VM_GFP_DEBUG=18, /* debug GFP failures */ + VM_PAGEBUF=22, /* struct: Control pagebuf parameters */ + VM_HUGETLB_POOL=23, /* int: size of the hugetlb pool, in MB */ + VM_DCACHE_PRIORITY=24, /* int: priority of the dcache pool */ +Index: linux-2.4.21-rhel/include/linux/mm.h +=================================================================== +--- linux-2.4.21-rhel.orig/include/linux/mm.h 2005-05-31 18:50:32.000000000 -0600 ++++ linux-2.4.21-rhel/include/linux/mm.h 2005-05-31 18:50:45.000000000 -0600 +@@ -846,6 +846,7 @@ + #define __GFP_HIGHIO 0x80 /* Can start high mem physical IO? */ + #define __GFP_FS 0x100 /* Can call down to low-level FS? */ + #define __GFP_WIRED 0x200 /* Highmem bias and wired */ ++#define __GFP_NOWARN 0x400 /* Don't report error on allocation failure */ + + #define GFP_NOHIGHIO (__GFP_HIGH | __GFP_WAIT | __GFP_IO) + #define GFP_NOIO (__GFP_HIGH | __GFP_WAIT) +Index: linux-2.4.21-rhel/include/linux/swap.h +=================================================================== +--- linux-2.4.21-rhel.orig/include/linux/swap.h 2005-03-18 18:25:26.000000000 -0700 ++++ linux-2.4.21-rhel/include/linux/swap.h 2005-05-31 18:52:44.000000000 -0600 +@@ -178,6 +178,7 @@ + extern int rebalance_inactive(int); + extern void wakeup_kswapd(unsigned int); + extern void rss_free_pages(unsigned int); ++extern int vm_gfp_debug; + + /* + * Limits, in percent, on how large the cache can be and how to do diff --git a/lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch b/lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch new file mode 100644 index 0000000..94c50d0 --- /dev/null +++ b/lustre/kernel_patches/patches/grab_cache_page_nowait_gfp-2.4.21-suse2.patch @@ -0,0 +1,85 @@ +Index: linux-2.4.21-suse2/mm/filemap.c +=================================================================== +--- linux-2.4.21-suse2.orig/mm/filemap.c 2005-04-04 05:58:21.000000000 -0600 ++++ linux-2.4.21-suse2/mm/filemap.c 2005-04-04 06:18:57.000000000 -0600 +@@ -1022,6 +1022,14 @@ + */ + struct page *grab_cache_page_nowait(struct address_space *mapping, unsigned long index) + { ++ return grab_cache_page_nowait_gfp(mapping, index, mapping->gfp_mask); ++} ++ ++ ++struct page *grab_cache_page_nowait_gfp(struct address_space *mapping, ++ unsigned long index, ++ unsigned int gfp_mask) ++{ + struct page *page, **hash; + + hash = page_hash(mapping, index); +@@ -1046,7 +1054,7 @@ + } + } + +- page = page_cache_alloc(mapping); ++ page = alloc_pages(gfp_mask, 0); + if ( unlikely(!page) ) + return NULL; /* Failed to allocate a page */ + +Index: linux-2.4.21-suse2/mm/page_alloc.c +=================================================================== +--- linux-2.4.21-suse2.orig/mm/page_alloc.c 2005-04-04 05:58:04.000000000 -0600 ++++ linux-2.4.21-suse2/mm/page_alloc.c 2005-04-04 06:12:11.000000000 -0600 +@@ -435,7 +435,7 @@ + break; + + min = z->watermarks[class_idx].min; +- if (!(gfp_mask & __GFP_WAIT)) ++ if (!(gfp_mask & __GFP_WAIT) && (gfp_mask & __GFP_HIGH)) + min >>= 2; + else if (current->rt_priority) + min >>= 1; +@@ -504,6 +504,7 @@ + } + + out: ++ if (!(gfp_mask & __GFP_NOWARN)) + printk(KERN_NOTICE "__alloc_pages: %u-order allocation failed (gfp=0x%x/%i)\n", + order, gfp_mask, !!(current->flags & PF_MEMALLOC)); + if (unlikely(vm_gfp_debug)) +Index: linux-2.4.21-suse2/kernel/ksyms.c +=================================================================== +--- linux-2.4.21-suse2.orig/kernel/ksyms.c 2005-04-04 06:01:39.000000000 -0600 ++++ linux-2.4.21-suse2/kernel/ksyms.c 2005-04-04 06:01:43.000000000 -0600 +@@ -326,6 +326,7 @@ + EXPORT_SYMBOL(__find_lock_page); + EXPORT_SYMBOL(find_or_create_page); + EXPORT_SYMBOL(grab_cache_page_nowait); ++EXPORT_SYMBOL(grab_cache_page_nowait_gfp); + EXPORT_SYMBOL(read_cache_page); + EXPORT_SYMBOL(set_page_dirty); + EXPORT_SYMBOL(vfs_readlink); +Index: linux-2.4.21-suse2/include/linux/pagemap.h +=================================================================== +--- linux-2.4.21-suse2.orig/include/linux/pagemap.h 2003-11-10 17:44:33.000000000 -0700 ++++ linux-2.4.21-suse2/include/linux/pagemap.h 2005-04-04 06:01:43.000000000 -0600 +@@ -110,6 +110,7 @@ + + + extern struct page * grab_cache_page_nowait (struct address_space *, unsigned long); ++extern struct page * grab_cache_page_nowait_gfp (struct address_space *, unsigned long, unsigned int); + + typedef int filler_t(void *, struct page*); + +Index: linux-2.4.21-suse2/include/linux/mm.h +=================================================================== +--- linux-2.4.21-suse2.orig/include/linux/mm.h 2005-04-04 05:58:14.000000000 -0600 ++++ linux-2.4.21-suse2/include/linux/mm.h 2005-04-04 06:17:10.000000000 -0600 +@@ -661,6 +661,7 @@ + #define __GFP_IO 0x40 /* Can start low memory physical IO? */ + #define __GFP_HIGHIO 0x80 /* Can start high mem physical IO? */ + #define __GFP_FS 0x100 /* Can call down to low-level FS? */ ++#define __GFP_NOWARN 0x200 /* Don't warn on allocation failure */ + + #define GFP_NOHIGHIO (__GFP_HIGH | __GFP_WAIT | __GFP_IO) + #define GFP_NOIO (__GFP_HIGH | __GFP_WAIT) diff --git a/lustre/kernel_patches/patches/invalidate_show-2.4.29.patch b/lustre/kernel_patches/patches/invalidate_show-2.4.29.patch new file mode 100644 index 0000000..dedc90b --- /dev/null +++ b/lustre/kernel_patches/patches/invalidate_show-2.4.29.patch @@ -0,0 +1,107 @@ +Index: linux-2.4.29/fs/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/inode.c 2005-04-07 18:55:16.732416736 +0300 ++++ linux-2.4.29/fs/inode.c 2005-04-07 19:16:46.772300864 +0300 +@@ -670,7 +670,8 @@ + /* + * Invalidate all inodes for a device. + */ +-static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose) ++static int invalidate_list(struct list_head *head, struct super_block * sb, ++ struct list_head * dispose, int show) + { + struct list_head *next; + int busy = 0, count = 0; +@@ -695,6 +696,11 @@ + count++; + continue; + } ++ if (show) ++ printk(KERN_ERR ++ "inode busy: dev %s:%lu (%p) mode %o count %u\n", ++ kdevname(sb->s_dev), inode->i_ino, inode, ++ inode->i_mode, atomic_read(&inode->i_count)); + busy = 1; + } + /* only unused inodes may be cached with i_count zero */ +@@ -719,17 +725,17 @@ + * If the discard is successful all the inodes have been discarded. + */ + +-int invalidate_inodes(struct super_block * sb) ++int invalidate_inodes(struct super_block * sb, int show) + { + int busy; + LIST_HEAD(throw_away); + + spin_lock(&inode_lock); +- busy = invalidate_list(&inode_in_use, sb, &throw_away); +- busy |= invalidate_list(&inode_unused, sb, &throw_away); +- busy |= invalidate_list(&inode_unused_pagecache, sb, &throw_away); +- busy |= invalidate_list(&sb->s_dirty, sb, &throw_away); +- busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away); ++ busy = invalidate_list(&inode_in_use, sb, &throw_away, show); ++ busy |= invalidate_list(&inode_unused, sb, &throw_away, show); ++ busy |= invalidate_list(&inode_unused_pagecache, sb, &throw_away, show); ++ busy |= invalidate_list(&sb->s_dirty, sb, &throw_away, show); ++ busy |= invalidate_list(&sb->s_locked_inodes, sb, &throw_away, show); + spin_unlock(&inode_lock); + + dispose_list(&throw_away); +@@ -755,7 +761,7 @@ + * hold). + */ + shrink_dcache_sb(sb); +- res = invalidate_inodes(sb); ++ res = invalidate_inodes(sb, 0); + drop_super(sb); + } + invalidate_buffers(dev); +Index: linux-2.4.29/fs/super.c +=================================================================== +--- linux-2.4.29.orig/fs/super.c 2005-04-07 18:53:30.978493776 +0300 ++++ linux-2.4.29/fs/super.c 2005-04-07 19:14:26.187672976 +0300 +@@ -844,7 +844,7 @@ + lock_super(sb); + lock_kernel(); + sb->s_flags &= ~MS_ACTIVE; +- invalidate_inodes(sb); /* bad name - it should be evict_inodes() */ ++ invalidate_inodes(sb, 0); /* bad name - it should be evict_inodes() */ + if (sop) { + if (sop->write_super && sb->s_dirt) + sop->write_super(sb); +@@ -853,7 +853,7 @@ + } + + /* Forget any remaining inodes */ +- if (invalidate_inodes(sb)) { ++ if (invalidate_inodes(sb, 1)) { + printk(KERN_ERR "VFS: Busy inodes after unmount. " + "Self-destruct in 5 seconds. Have a nice day...\n"); + } +Index: linux-2.4.29/include/linux/fs.h +=================================================================== +--- linux-2.4.29.orig/include/linux/fs.h 2005-04-07 19:14:06.319693368 +0300 ++++ linux-2.4.29/include/linux/fs.h 2005-04-07 19:14:26.190672520 +0300 +@@ -1286,7 +1286,7 @@ + extern int get_buffer_flushtime(void); + extern void balance_dirty(void); + extern int check_disk_change(kdev_t); +-extern int invalidate_inodes(struct super_block *); ++extern int invalidate_inodes(struct super_block *, int); + extern int invalidate_device(kdev_t, int); + extern void invalidate_inode_pages(struct inode *); + extern void invalidate_inode_pages2(struct address_space *); +Index: linux-2.4.29/fs/smbfs/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/smbfs/inode.c 2005-04-07 18:52:37.889564520 +0300 ++++ linux-2.4.29/fs/smbfs/inode.c 2005-04-07 19:14:26.192672216 +0300 +@@ -175,7 +175,7 @@ + { + VERBOSE("\n"); + shrink_dcache_sb(SB_of(server)); +- invalidate_inodes(SB_of(server)); ++ invalidate_inodes(SB_of(server), 0); + } + + /* diff --git a/lustre/kernel_patches/patches/kallsyms-2.4.29.patch b/lustre/kernel_patches/patches/kallsyms-2.4.29.patch new file mode 100644 index 0000000..d270aa4 --- /dev/null +++ b/lustre/kernel_patches/patches/kallsyms-2.4.29.patch @@ -0,0 +1,689 @@ +Index: linux-2.4.29/arch/arm/vmlinux-armo.lds.in +=================================================================== +--- linux-2.4.29.orig/arch/arm/vmlinux-armo.lds.in 2005-05-08 23:06:26.916055656 +0300 ++++ linux-2.4.29/arch/arm/vmlinux-armo.lds.in 2005-05-08 23:07:11.214321296 +0300 +@@ -62,6 +62,10 @@ + *(__ksymtab) + __stop___ksymtab = .; + ++ __start___kallsyms = .; /* All kernel symbols */ ++ *(__kallsyms) ++ __stop___kallsyms = .; ++ + *(.got) /* Global offset table */ + + _etext = .; /* End of text section */ +Index: linux-2.4.29/arch/arm/vmlinux-armv.lds.in +=================================================================== +--- linux-2.4.29.orig/arch/arm/vmlinux-armv.lds.in 2005-05-08 23:06:26.917055504 +0300 ++++ linux-2.4.29/arch/arm/vmlinux-armv.lds.in 2005-05-08 23:07:11.215321144 +0300 +@@ -67,6 +67,12 @@ + __stop___ksymtab = .; + } + ++ __kallsyms : { /* Kernel debugging table */ ++ __start___kallsyms = .; /* All kernel symbols */ ++ *(__kallsyms) ++ __stop___kallsyms = .; ++ } ++ + . = ALIGN(8192); + + .data : { +Index: linux-2.4.29/arch/ppc/config.in +=================================================================== +--- linux-2.4.29.orig/arch/ppc/config.in 2005-05-08 23:06:26.933053072 +0300 ++++ linux-2.4.29/arch/ppc/config.in 2005-05-08 23:07:11.216320992 +0300 +@@ -655,6 +655,7 @@ + fi + fi + fi ++bool 'Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS + + if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool 'Support for early boot text console (BootX or OpenFirmware only)' CONFIG_BOOTX_TEXT +Index: linux-2.4.29/arch/ppc/vmlinux.lds +=================================================================== +--- linux-2.4.29.orig/arch/ppc/vmlinux.lds 2005-05-08 23:06:26.934052920 +0300 ++++ linux-2.4.29/arch/ppc/vmlinux.lds 2005-05-08 23:07:11.217320840 +0300 +@@ -74,6 +74,10 @@ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + ++ __start___kallsyms = .; /* All kernel symbols */ ++ __kallsyms : { *(__kallsyms) } ++ __stop___kallsyms = .; ++ + . = ALIGN(8); + __start___ftr_fixup = .; + __ftr_fixup : { *(__ftr_fixup) } +Index: linux-2.4.29/arch/i386/config.in +=================================================================== +--- linux-2.4.29.orig/arch/i386/config.in 2005-05-08 23:07:09.946514032 +0300 ++++ linux-2.4.29/arch/i386/config.in 2005-05-08 23:33:00.395809912 +0300 +@@ -512,6 +512,7 @@ + bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ + bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK + bool ' Compile the kernel with frame pointers' CONFIG_FRAME_POINTER ++ bool ' Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS + fi + + int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 +Index: linux-2.4.29/arch/ia64/config.in +=================================================================== +--- linux-2.4.29.orig/arch/ia64/config.in 2005-05-08 23:06:26.936052616 +0300 ++++ linux-2.4.29/arch/ia64/config.in 2005-05-08 23:07:11.219320536 +0300 +@@ -318,4 +318,6 @@ + + int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 + ++bool ' Load all symbols for debugging/kksymoops' CONFIG_KALLSYMS ++ + endmenu +Index: linux-2.4.29/arch/alpha/vmlinux.lds.in +=================================================================== +--- linux-2.4.29.orig/arch/alpha/vmlinux.lds.in 2005-05-08 23:06:26.937052464 +0300 ++++ linux-2.4.29/arch/alpha/vmlinux.lds.in 2005-05-08 23:07:11.220320384 +0300 +@@ -28,6 +28,10 @@ + __stop___ksymtab = .; + .kstrtab : { *(.kstrtab) } + ++ __start___kallsyms = .; /* All kernel symbols */ ++ __kallsyms : { *(__kallsyms) } ++ __stop___kallsyms = .; ++ + /* Startup code */ + . = ALIGN(8192); + __init_begin = .; +Index: linux-2.4.29/Makefile +=================================================================== +--- linux-2.4.29.orig/Makefile 2005-05-08 22:59:19.203077912 +0300 ++++ linux-2.4.29/Makefile 2005-05-08 23:07:11.222320080 +0300 +@@ -37,6 +37,7 @@ + MAKEFILES = $(TOPDIR)/.config + GENKSYMS = /sbin/genksyms + DEPMOD = /sbin/depmod ++KALLSYMS = /sbin/kallsyms + MODFLAGS = -DMODULE + CFLAGS_KERNEL = + PERL = perl +@@ -44,6 +45,8 @@ + RPM := $(shell if [ -x "/usr/bin/rpmbuild" ]; then echo rpmbuild; \ + else echo rpm; fi) + ++TMPPREFIX = ++ + export VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \ + CONFIG_SHELL TOPDIR HPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ + CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS MODFLAGS PERL AWK +@@ -202,7 +205,7 @@ + CLEAN_FILES = \ + kernel/ksyms.lst include/linux/compile.h \ + vmlinux System.map \ +- .tmp* \ ++ $(TMPPREFIX).tmp* \ + drivers/char/consolemap_deftbl.c drivers/video/promcon_tbl.c \ + drivers/char/conmakehash \ + drivers/char/drm/*-mod.c \ +@@ -285,16 +288,39 @@ + boot: vmlinux + @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C arch/$(ARCH)/boot + ++LD_VMLINUX := $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \ ++ --start-group \ ++ $(CORE_FILES) \ ++ $(DRIVERS) \ ++ $(NETWORKS) \ ++ $(LIBS) \ ++ --end-group ++ifeq ($(CONFIG_KALLSYMS),y) ++LD_VMLINUX_KALLSYMS := $(TMPPREFIX).tmp_kallsyms3.o ++else ++LD_VMLINUX_KALLSYMS := ++endif ++ + vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o init/do_mounts.o linuxsubdirs +- $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o init/do_mounts.o \ +- --start-group \ +- $(CORE_FILES) \ +- $(DRIVERS) \ +- $(NETWORKS) \ +- $(LIBS) \ +- --end-group \ +- -o vmlinux ++ @$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" kallsyms ++ ++.PHONY: kallsyms ++ ++kallsyms: ++ifeq ($(CONFIG_KALLSYMS),y) ++ @echo kallsyms pass 1 ++ $(LD_VMLINUX) -o $(TMPPREFIX).tmp_vmlinux1 ++ @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux1 > $(TMPPREFIX).tmp_kallsyms1.o ++ @echo kallsyms pass 2 ++ @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms1.o -o $(TMPPREFIX).tmp_vmlinux2 ++ @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux2 > $(TMPPREFIX).tmp_kallsyms2.o ++ @echo kallsyms pass 3 ++ @$(LD_VMLINUX) $(TMPPREFIX).tmp_kallsyms2.o -o $(TMPPREFIX).tmp_vmlinux3 ++ @$(KALLSYMS) $(TMPPREFIX).tmp_vmlinux3 > $(TMPPREFIX).tmp_kallsyms3.o ++endif ++ $(LD_VMLINUX) $(LD_VMLINUX_KALLSYMS) -o vmlinux + $(NM) vmlinux | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map ++ @rm -f $(TMPPREFIX).tmp_vmlinux* $(TMPPREFIX).tmp_kallsyms* + + symlinks: + rm -f include/asm +Index: linux-2.4.29/kernel/Makefile +=================================================================== +--- linux-2.4.29.orig/kernel/Makefile 2005-05-08 23:06:26.939052160 +0300 ++++ linux-2.4.29/kernel/Makefile 2005-05-08 23:07:11.223319928 +0300 +@@ -19,6 +19,7 @@ + obj-$(CONFIG_UID16) += uid16.o + obj-$(CONFIG_MODULES) += ksyms.o + obj-$(CONFIG_PM) += pm.o ++obj-$(CONFIG_KALLSYMS) += kallsyms.o + + ifneq ($(CONFIG_IA64),y) + # According to Alan Modra , 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 + #endif ++#ifdef CONFIG_KALLSYMS ++#include ++#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 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 ++#include ++#include ++#include ++#include ++ ++/* 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 ++ ++ 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 ++#else /* __KERNEL__ */ ++#include ++#endif /* __KERNEL__ */ ++#endif /* ELFCLASS32 */ ++ ++#ifndef ELFCLASSM ++#define ELFCLASSM ELF_CLASS ++#endif ++ ++#ifndef ElfW ++# if ELFCLASSM == ELFCLASS32 ++# define ElfW(x) Elf32_ ## x ++# define ELFW(x) ELF32_ ## x ++# else ++# define ElfW(x) Elf64_ ## x ++# define ELFW(x) ELF64_ ## x ++# endif ++#endif ++ ++/* Format of data in the kallsyms section. ++ * Most of the fields are small numbers but the total size and all ++ * offsets can be large so use the 32/64 bit types for these fields. ++ * ++ * Do not use sizeof() on these structures, modutils may be using extra ++ * fields. Instead use the size fields in the header to access the ++ * other bits of data. ++ */ ++ ++struct kallsyms_header { ++ int size; /* Size of this header */ ++ ElfW(Word) total_size; /* Total size of kallsyms data */ ++ int sections; /* Number of section entries */ ++ ElfW(Off) section_off; /* Offset to first section entry */ ++ int section_size; /* Size of one section entry */ ++ int symbols; /* Number of symbol entries */ ++ ElfW(Off) symbol_off; /* Offset to first symbol entry */ ++ int symbol_size; /* Size of one symbol entry */ ++ ElfW(Off) string_off; /* Offset to first string */ ++ ElfW(Addr) start; /* Start address of first section */ ++ ElfW(Addr) end; /* End address of last section */ ++}; ++ ++struct kallsyms_section { ++ ElfW(Addr) start; /* Start address of section */ ++ ElfW(Word) size; /* Size of this section */ ++ ElfW(Off) name_off; /* Offset to section name */ ++ ElfW(Word) flags; /* Flags from section */ ++}; ++ ++struct kallsyms_symbol { ++ ElfW(Off) section_off; /* Offset to section that owns this symbol */ ++ ElfW(Addr) symbol_addr; /* Address of symbol */ ++ ElfW(Off) name_off; /* Offset to symbol name */ ++}; ++ ++#define KALLSYMS_SEC_NAME "__kallsyms" ++#define KALLSYMS_IDX 2 /* obj_kallsyms creates kallsyms as section 2 */ ++ ++#define kallsyms_next_sec(h,s) \ ++ ((s) = (struct kallsyms_section *)((char *)(s) + (h)->section_size)) ++#define kallsyms_next_sym(h,s) \ ++ ((s) = (struct kallsyms_symbol *)((char *)(s) + (h)->symbol_size)) ++ ++int kallsyms_symbol_to_address( ++ const char *name, /* Name to lookup */ ++ unsigned long *token, /* Which module to start with */ ++ const char **mod_name, /* Set to module name or "kernel" */ ++ unsigned long *mod_start, /* Set to start address of module */ ++ unsigned long *mod_end, /* Set to end address of module */ ++ const char **sec_name, /* Set to section name */ ++ unsigned long *sec_start, /* Set to start address of section */ ++ unsigned long *sec_end, /* Set to end address of section */ ++ const char **sym_name, /* Set to full symbol name */ ++ unsigned long *sym_start, /* Set to start address of symbol */ ++ unsigned long *sym_end /* Set to end address of symbol */ ++ ); ++ ++int kallsyms_address_to_symbol( ++ unsigned long address, /* Address to lookup */ ++ const char **mod_name, /* Set to module name */ ++ unsigned long *mod_start, /* Set to start address of module */ ++ unsigned long *mod_end, /* Set to end address of module */ ++ const char **sec_name, /* Set to section name */ ++ unsigned long *sec_start, /* Set to start address of section */ ++ unsigned long *sec_end, /* Set to end address of section */ ++ const char **sym_name, /* Set to full symbol name */ ++ unsigned long *sym_start, /* Set to start address of symbol */ ++ unsigned long *sym_end /* Set to end address of symbol */ ++ ); ++ ++int kallsyms_sections(void *token, ++ int (*callback)(void *, /* token */ ++ const char *, /* module name */ ++ const char *, /* section name */ ++ ElfW(Addr), /* Section start */ ++ ElfW(Addr), /* Section end */ ++ ElfW(Word) /* Section flags */ ++ ) ++ ); ++ ++#endif /* kallsyms.h */ +Index: linux-2.4.29/arch/i386/vmlinux.lds.S +=================================================================== +--- linux-2.4.29.orig/arch/i386/vmlinux.lds.S 2005-05-08 23:07:09.948513728 +0300 ++++ linux-2.4.29/arch/i386/vmlinux.lds.S 2005-05-08 23:14:24.128508336 +0300 +@@ -28,6 +28,10 @@ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + ++ __start___kallsyms = .; /* All kernel symbols */ ++ __kallsyms : { *(__kallsyms) } ++ __stop___kallsyms = .; ++ + .data : { /* Data */ + *(.data) + CONSTRUCTORS diff --git a/lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch b/lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch new file mode 100644 index 0000000..ff2991f --- /dev/null +++ b/lustre/kernel_patches/patches/linux-2.4.24-jbd-handle-EIO.patch @@ -0,0 +1,51 @@ +diff -X /home/nikita/src/linux-git/linux-2.6.git/Documentation/dontdiff -rupbB linux-2.4.24.orig/fs/jbd/commit.c linux-2.4.24/fs/jbd/commit.c +--- linux-2.4.24.orig/fs/jbd/commit.c 2005-06-23 17:39:32.000000000 +0400 ++++ linux-2.4.24/fs/jbd/commit.c 2005-06-23 15:56:05.000000000 +0400 +@@ -47,7 +47,7 @@ void journal_commit_transaction(journal_ + struct buffer_head *wbuf[64]; + int bufs; + int flags; +- int err; ++ int err = 0; + unsigned long blocknr; + char *tagp = NULL; + journal_header_t *header; +@@ -505,6 +505,9 @@ start_journal_io: + goto wait_for_iobuf; + } + ++ if (unlikely(!buffer_uptodate(bh))) ++ err = -EIO; ++ + clear_bit(BH_JWrite, &jh2bh(jh)->b_state); + + JBUFFER_TRACE(jh, "ph4: unfile after journal write"); +@@ -566,6 +569,9 @@ start_journal_io: + goto wait_for_ctlbuf; + } + ++ if (unlikely(!buffer_uptodate(bh))) ++ err = -EIO; ++ + BUFFER_TRACE(bh, "ph5: control buffer writeout done: unfile"); + clear_bit(BH_JWrite, &bh->b_state); + journal_unfile_buffer(jh); +@@ -610,6 +616,8 @@ start_journal_io: + bh->b_end_io = journal_end_buffer_io_sync; + submit_bh(WRITE, bh); + wait_on_buffer(bh); ++ if (unlikely(!buffer_uptodate(bh))) ++ err = -EIO; + put_bh(bh); /* One for getblk() */ + journal_unlock_journal_head(descriptor); + } +@@ -621,6 +629,9 @@ start_journal_io: + + skip_commit: /* The journal should be unlocked by now. */ + ++ if (err) ++ __journal_abort_hard(journal); ++ + /* Call any callbacks that had been registered for handles in this + * transaction. It is up to the callback to free any allocated + * memory. diff --git a/lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch b/lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch new file mode 100644 index 0000000..8225ea3 --- /dev/null +++ b/lustre/kernel_patches/patches/linux-2.4.29-xattr-0.8.54.patch @@ -0,0 +1,5362 @@ +Index: linux-2.4.29/Documentation/Configure.help +=================================================================== +--- linux-2.4.29.orig/Documentation/Configure.help 2005-04-07 18:55:00.000000000 +0300 ++++ linux-2.4.29/Documentation/Configure.help 2005-05-03 17:59:40.363127040 +0300 +@@ -16679,6 +16679,39 @@ + be compiled as a module, and so this could be dangerous. Most + everyone wants to say Y here. + ++Ext2 extended attributes ++CONFIG_EXT2_FS_XATTR ++ Extended attributes are name:value pairs associated with inodes by ++ the kernel or by users (see the attr(5) manual page, or visit ++ 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 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 ++ 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 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 + #include ++#include + #include + + /* +@@ -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 + #include + #include ++#include + #include + #include + +@@ -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 + #include ++#include + #include + + /* +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include ++#include + + 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, ++ * ++ * Fix by Harrison Xing . ++ * Extended attributes for symlinks and special files added per ++ * suggestion of Luka Renko . ++ */ ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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; iprefix); ++ 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, ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_EXT2_FS_POSIX_ACL ++# include ++#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 + #include + #include ++#include + #include + #include + +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + + 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, ++ * ++ * Fix by Harrison Xing . ++ * Ext3 code with a lot of help from Eric Jarman . ++ * Extended attributes for symlinks and special files added per ++ * suggestion of Luka Renko . ++ */ ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; iprefix); ++ 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, ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_EXT3_FS_POSIX_ACL ++# include ++#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 ++#include ++#include ++#include ++#include ++ ++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, ++ */ ++ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++#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 "); ++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; ne_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; nc_block_hash[n]); ++ for (m=0; mc_indexes_hash[m] = kmalloc(bucket_count * ++ sizeof(struct list_head), ++ GFP_KERNEL); ++ if (!cache->c_indexes_hash[m]) ++ goto fail; ++ for (n=0; nc_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; ne_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<=indexc_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<=indexe_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, ++ */ ++ ++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, ++*/ ++ ++#include ++#include ++#include ++ ++/* 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<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, ++*/ ++ ++#include ++#include ++#include ++ ++/* 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<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, ++*/ ++ ++/* 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 + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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); diff --git a/lustre/kernel_patches/patches/lustre_version.patch b/lustre/kernel_patches/patches/lustre_version.patch index aa8c735..6a9810b 100644 --- a/lustre/kernel_patches/patches/lustre_version.patch +++ b/lustre/kernel_patches/patches/lustre_version.patch @@ -1,3 +1,4 @@ +Version 46: Separate ->fs_private in struct file Version 45: more robust and general dev_read_only for failover (b=4834) Version 44: fix link_path_walk_it() oops creating .foo in deleted "." (b=5548) Version 43: fix remove_suid to not crash 2.6, and do anything on 2.4 (b=5695) @@ -19,6 +20,6 @@ Version 34: ext3 iopen assertion (b=2517), operations on deleted "." (b=2399) --- /dev/null Fri Aug 30 17:31:37 2002 +++ linux-2.4.18-18.8.0-l12-braam/include/linux/lustre_version.h Thu Feb 13 07:58:33 2003 @@ -0,0 +1 @@ -+#define LUSTRE_KERNEL_VERSION 45 ++#define LUSTRE_KERNEL_VERSION 46 _ diff --git a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.20-hp.patch b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.20-hp.patch index a8ad4a2..52ea4e1 100644 --- a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.20-hp.patch +++ b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.20-hp.patch @@ -285,7 +285,7 @@ --- 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; @@ -384,6 +384,9 @@ + return err; +} + ++#ifndef O_OWNER_OVERRIDE ++#define O_OWNER_OVERRIDE 0200000000 ++#endif +static int setattr_raw(struct inode *inode, struct iattr *iap) +{ + int err; @@ -417,9 +420,11 @@ 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); } @@ -432,13 +437,10 @@ int err; /* If we get here, then the client has already done an "open", and (hopefully) -@@ -473,6 +599,18 @@ nfsd_open(struct svc_rqst *rqstp, struct +@@ -473,6 +599,15 @@ nfsd_open(struct svc_rqst *rqstp, struct filp->f_mode = FMODE_READ; } -+#ifndef O_OWNER_OVERRIDE -+#define O_OWNER_OVERRIDE 0200000000 -+#endif + intent_init(&it, IT_OPEN, (filp->f_flags & ~O_ACCMODE) | filp->f_mode | + O_OWNER_OVERRIDE); + diff --git a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch new file mode 100644 index 0000000..4708b75 --- /dev/null +++ b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch @@ -0,0 +1,743 @@ +Index: linux-2.4.29/fs/Makefile +=================================================================== +--- linux-2.4.29.orig/fs/Makefile 2005-05-03 18:16:44.000000000 +0300 ++++ linux-2.4.29/fs/Makefile 2005-05-03 18:46:09.301144016 +0300 +@@ -7,7 +7,8 @@ + + O_TARGET := fs.o + +-export-objs := filesystems.o open.o dcache.o buffer.o dquot.o inode.o ++export-objs := filesystems.o open.o dcache.o buffer.o dquot.o inode.o \ ++ namei.o file_table.o + mod-subdirs := nls + + obj-y := open.o read_write.o devices.o file_table.o buffer.o \ +Index: linux-2.4.29/fs/file_table.c +=================================================================== +--- linux-2.4.29.orig/fs/file_table.c 2005-05-03 16:28:21.000000000 +0300 ++++ linux-2.4.29/fs/file_table.c 2005-05-03 18:46:09.303143712 +0300 +@@ -82,7 +82,8 @@ + * and call the open function (if any). The caller must verify that + * inode->i_fop is not NULL. + */ +-int init_private_file(struct file *filp, struct dentry *dentry, int mode) ++int init_private_file_it(struct file *filp, struct dentry *dentry, int mode, ++ struct lookup_intent *it) + { + memset(filp, 0, sizeof(*filp)); + filp->f_mode = mode; +@@ -90,12 +91,20 @@ + filp->f_dentry = dentry; + filp->f_uid = current->fsuid; + filp->f_gid = current->fsgid; ++ if (it) ++ filp->f_it = it; + filp->f_op = dentry->d_inode->i_fop; + if (filp->f_op->open) + return filp->f_op->open(dentry->d_inode, filp); + else + return 0; + } ++EXPORT_SYMBOL(init_private_file_it); ++ ++int init_private_file(struct file *filp, struct dentry *dentry, int mode) ++{ ++ return init_private_file_it(filp, dentry, mode, NULL); ++} + + void fastcall fput(struct file * file) + { +Index: linux-2.4.29/fs/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/inode.c 2005-05-03 18:16:44.000000000 +0300 ++++ linux-2.4.29/fs/inode.c 2005-05-03 18:51:36.389419040 +0300 +@@ -1139,9 +1139,10 @@ + return inode; + } + +-struct inode *iget4_locked(struct super_block *sb, unsigned long ino, find_inode_t find_actor, void *opaque) ++static inline struct inode *ifind(struct super_block *sb, unsigned long ino, ++ struct list_head *head, ++ find_inode_t find_actor, void *opaque) + { +- struct list_head * head = inode_hashtable + hash(sb,ino); + struct inode * inode; + + spin_lock(&inode_lock); +@@ -1154,6 +1155,24 @@ + } + spin_unlock(&inode_lock); + ++ return NULL; ++} ++ ++struct inode *ilookup4(struct super_block *sb, unsigned long ino, ++ find_inode_t find_actor, void *opaque) ++{ ++ struct list_head * head = inode_hashtable + hash(sb,ino); ++ return ifind(sb, ino, head, find_actor, opaque); ++} ++ ++struct inode *iget4_locked(struct super_block *sb, unsigned long ino, ++ find_inode_t find_actor, void *opaque) ++{ ++ struct list_head * head = inode_hashtable + hash(sb,ino); ++ struct inode *inode = ifind(sb, ino, head, find_actor, opaque); ++ if (inode) ++ return inode; ++ + /* + * get_new_inode() will do the right thing, re-trying the search + * in case it had to block at any point. +Index: linux-2.4.29/fs/namei.c +=================================================================== +--- linux-2.4.29.orig/fs/namei.c 2005-05-03 18:16:43.000000000 +0300 ++++ linux-2.4.29/fs/namei.c 2005-05-03 18:46:09.310142648 +0300 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -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); diff --git a/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 new file mode 100644 index 0000000..7bc7e44 --- /dev/null +++ b/lustre/kernel_patches/patches/nfs_export_kernel-2.4.29.patch-1 @@ -0,0 +1,730 @@ +Index: linux-2.4.29/fs/Makefile +=================================================================== +--- linux-2.4.29.orig/fs/Makefile 2005-04-07 19:31:00.000000000 +0300 ++++ linux-2.4.29/fs/Makefile 2005-05-03 15:59:07.943621928 +0300 +@@ -7,7 +7,8 @@ + + O_TARGET := fs.o + +-export-objs := filesystems.o open.o dcache.o buffer.o dquot.o inode.o ++export-objs := filesystems.o open.o dcache.o buffer.o dquot.o inode.o \ ++ namei.o file_table.o + mod-subdirs := nls + + obj-y := open.o read_write.o devices.o file_table.o buffer.o \ +Index: linux-2.4.29/fs/file_table.c +=================================================================== +--- linux-2.4.29.orig/fs/file_table.c 2005-04-07 18:52:26.000000000 +0300 ++++ linux-2.4.29/fs/file_table.c 2005-05-03 15:59:07.945621624 +0300 +@@ -82,7 +82,8 @@ + * and call the open function (if any). The caller must verify that + * inode->i_fop is not NULL. + */ +-int init_private_file(struct file *filp, struct dentry *dentry, int mode) ++int init_private_file_it(struct file *filp, struct dentry *dentry, int mode, ++ struct lookup_intent *it) + { + memset(filp, 0, sizeof(*filp)); + filp->f_mode = mode; +@@ -90,12 +91,20 @@ + filp->f_dentry = dentry; + filp->f_uid = current->fsuid; + filp->f_gid = current->fsgid; ++ if (it) ++ filp->f_it = it; + filp->f_op = dentry->d_inode->i_fop; + if (filp->f_op->open) + return filp->f_op->open(dentry->d_inode, filp); + else + return 0; + } ++EXPORT_SYMBOL(init_private_file_it); ++ ++int init_private_file(struct file *filp, struct dentry *dentry, int mode) ++{ ++ return init_private_file_it(filp, dentry, mode, NULL); ++} + + void fastcall fput(struct file * file) + { +Index: linux-2.4.29/fs/inode.c +=================================================================== +--- linux-2.4.29.orig/fs/inode.c 2005-04-07 19:18:51.000000000 +0300 ++++ linux-2.4.29/fs/inode.c 2005-05-03 16:02:40.198354304 +0300 +@@ -1154,6 +1154,24 @@ + } + spin_unlock(&inode_lock); + ++ return NULL; ++} ++ ++struct inode *ilookup4(struct super_block *sb, unsigned long ino, ++ find_inode_t find_actor, void *opaque) ++{ ++ struct list_head * head = inode_hashtable + hash(sb,ino); ++ return ifind(sb, ino, head, find_actor, opaque); ++} ++ ++static inline struct inode *ifind(struct super_block *sb, unsigned long ino, ++ struct list_head *head, ++ find_inode_t find_actor, void *opaque) ++{ ++ struct inode *inode = ifind(sb, ino, head, find_actor, opaque); ++ if (inode) ++ return inode; ++ + /* + * get_new_inode() will do the right thing, re-trying the search + * in case it had to block at any point. +Index: linux-2.4.29/fs/namei.c +=================================================================== +--- linux-2.4.29.orig/fs/namei.c 2005-04-07 19:14:06.000000000 +0300 ++++ linux-2.4.29/fs/namei.c 2005-05-03 15:59:07.953620408 +0300 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -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); diff --git a/lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch b/lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch new file mode 100644 index 0000000..4bb8892 --- /dev/null +++ b/lustre/kernel_patches/patches/nfs_statfs-toomanyfiles-rhel-2.4.patch @@ -0,0 +1,30 @@ +Index: linux-2.4.21/fs/nfs/inode.c +=================================================================== +--- linux-2.4.21.orig/fs/nfs/inode.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/fs/nfs/inode.c 2005-06-01 23:38:54.883239912 -0400 +@@ -679,14 +679,21 @@ + goto too_big; + if (TOOBIG(((res.abytes + blockres) >> blockbits))) + goto too_big; +- if (TOOBIG(res.tfiles) || TOOBIG(res.afiles)) +- goto too_big; + + buf->f_blocks = (res.tbytes + blockres) >> blockbits; + buf->f_bfree = (res.fbytes + blockres) >> blockbits; + buf->f_bavail = (res.abytes + blockres) >> blockbits; +- buf->f_files = res.tfiles; +- buf->f_ffree = res.afiles; ++ ++ if (TOOBIG(res.tfiles)) ++ buf->f_files = -1; ++ else ++ buf->f_files = res.tfiles; ++ ++ if (TOOBIG(res.afiles)) ++ buf->f_ffree = -1; ++ else ++ buf->f_ffree = res.afiles; ++ + return 0; + + too_big: diff --git a/lustre/kernel_patches/patches/nfsd_iallocsem.patch b/lustre/kernel_patches/patches/nfsd_iallocsem.patch new file mode 100644 index 0000000..96b9c71 --- /dev/null +++ b/lustre/kernel_patches/patches/nfsd_iallocsem.patch @@ -0,0 +1,19 @@ +===== fs/nfsd/vfs.c 1.20 vs edited ===== +--- 1.20/fs/nfsd/vfs.c 2005-02-08 16:35:28 +02:00 ++++ edited/fs/nfsd/vfs.c 2005-05-29 00:46:44 +03:00 +@@ -297,6 +297,7 @@ + iap->ia_valid |= ATTR_CTIME; + + if (iap->ia_valid & ATTR_SIZE) { ++ down_write(&inode->i_alloc_sem); + fh_lock(fhp); + size_change = 1; + } +@@ -307,6 +308,7 @@ + } + if (size_change) { + fh_unlock(fhp); ++ up_write(&inode->i_alloc_sem); + put_write_access(inode); + } + if (!err) diff --git a/lustre/kernel_patches/patches/qsnet-rhel-2.4.patch b/lustre/kernel_patches/patches/qsnet-rhel-2.4.patch new file mode 100644 index 0000000..45e0ac5 --- /dev/null +++ b/lustre/kernel_patches/patches/qsnet-rhel-2.4.patch @@ -0,0 +1,93733 @@ +Index: linux-2.4.21/arch/i386/kernel/i386_ksyms.c +=================================================================== +--- linux-2.4.21.orig/arch/i386/kernel/i386_ksyms.c 2005-06-01 22:51:51.000000000 -0400 ++++ linux-2.4.21/arch/i386/kernel/i386_ksyms.c 2005-06-01 23:12:54.521450960 -0400 +@@ -220,3 +220,12 @@ + EXPORT_SYMBOL_GPL(__PAGE_KERNEL); + extern unsigned long long __supported_pte_mask; + EXPORT_SYMBOL_GPL(__supported_pte_mask); ++ ++extern asmlinkage long sys_open(const char *, int, int); ++EXPORT_SYMBOL(sys_open); ++extern asmlinkage off_t sys_lseek(unsigned int, off_t, unsigned int); ++EXPORT_SYMBOL(sys_lseek); ++extern asmlinkage long sys_poll(struct pollfd *, unsigned int, long); ++EXPORT_SYMBOL(sys_poll); ++extern asmlinkage long sys_kill(int, int); ++EXPORT_SYMBOL(sys_kill); +Index: linux-2.4.21/arch/ia64/kernel/ia64_ksyms.c +=================================================================== +--- linux-2.4.21.orig/arch/ia64/kernel/ia64_ksyms.c 2005-06-01 22:51:59.000000000 -0400 ++++ linux-2.4.21/arch/ia64/kernel/ia64_ksyms.c 2005-06-01 23:14:43.773842072 -0400 +@@ -207,3 +207,13 @@ + EXPORT_SYMBOL_GPL(show_mem); + EXPORT_SYMBOL_GPL(show_state); + EXPORT_SYMBOL_GPL(show_regs); ++ ++#define __KERNEL_SYSCALLS__ 1 ++#include ++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 ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef ASSERT ++#include ++#include ++ ++ ++ ++#include ++#include ++ ++#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 ++#include ++ ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++ ++#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 ++#endif ++#include ++#include ++ ++/* ++ * 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 ++#include ++ ++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;railattached[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;iattached[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;railattached[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;railnode.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;railnode.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 ++ ++#else ++ ++#include ++#include ++#include ++ ++#endif ++ ++#include ++ ++ ++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 ++#include ++ ++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 ++#include ++ ++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 ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++MODULE_AUTHOR("Quadrics Ltd."); ++MODULE_DESCRIPTION("Elan support module"); ++ ++MODULE_LICENSE("GPL"); ++ ++/* elanmod.c */ ++EXPORT_SYMBOL(elanmod_classify_cap); ++ ++/* bitmap.c */ ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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;islot_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 ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++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; jTrData[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 ++#include ++ ++caddr_t ++MiToName (int mi) ++{ ++ static char space[32]; ++ static struct { ++ int mi; ++ char *name; ++ } info[] = { ++#include ++ }; ++ 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef DIGITAL_UNIX ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++typedef xdrproc_t kxdrproc_t; ++#endif ++ ++#ifdef LINUX ++#include ++#include ++#include ++#include ++ ++#include ++#define SYS_NMLN __NEW_UTS_LEN ++#endif ++ ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++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, "\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, "")) ++ { ++ 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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;iStatus.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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++ ++#ifdef NO_ABI ++#include ++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 ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* 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 ++#include ++#include ++ ++#include ++#include ++#ifdef CONFIG_MTRR ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23) ++typedef void irqreturn_t; ++#endif ++# define IRQ_NONE ++# define IRQ_HANDLED ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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; iucq_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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * ++ * 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, "\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, "\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, "")) ++ { ++ 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, ""); ++ else ++ { ++ if (i2c_read (dev, I2C_TEMP_ADDR, 2, values) < 0) ++ len = sprintf (page, ""); ++ 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; idev_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; iroutes[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; iroutes[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; idev_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; iroutes[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; iroutes[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; idev_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; iroutes[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; iroutes[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 ++ ++#include ++#include ++#include ++ ++/*================================================================================*/ ++/* 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 ++ ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++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 ++#include ++ ++/* ++ * 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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "debug.h" ++#include "cm.h" ++#include ++ ++#include ++ ++#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 (""); ++ 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 ++ ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "debug.h" ++#include "cm.h" ++#include ++ ++#include ++ ++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, "\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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++ ++#include "conf_linux.h" ++ ++#include ++#include ++#include ++#include ++ ++/* 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 ++ ++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 ++ ++#include ++ ++#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, ""); ++ 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 ++ ++#include ++ ++#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 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 ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++#include ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 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 ++ ++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 ++#include ++#include ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++ ++#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;fnFrags;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 ++#include ++#include ++#include ++ ++#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 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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++#include "debug.h" ++#include "conf_linux.h" ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++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 ++ ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#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 */ ++#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 ++ ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#endif /* !defined(__ELAN3__) */ ++ ++#include ++ ++/* 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 ++#include ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#if !defined(__elan4__) ++#include ++#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 ++#include ++ ++#include ++ ++#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 ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++ ++#include ++ ++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 ++ ++#include ++ ++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 ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan3.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++#include "debug.h" ++#include "conf_linux.h" ++#include ++#include ++#include ++ ++#include ++ ++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, "\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, "\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, ""); ++ 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, "\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 ++ ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++#include ++ ++/******************************** 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 ++#include ++ ++/****************************************************************************************/ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan3.h" ++#include "epcomms_elan3.h" ++#include "debug.h" ++ ++#include ++#include ++ ++/****************************************************************************************/ ++#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 ++#include ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++#include ++ ++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 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 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 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 ++ ++#include ++ ++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 ++ ++#include ++ ++#include "kcomm_elan3.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++#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 ++#include ++#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 ++ ++#include "jtagdrv.h" ++#include ++ ++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 ++ ++/* 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 ++ ++#include ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++ ++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 0 ) ++ { ++ int i; ++ for(i=0;i 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 ++ ++#if defined(LINUX_I386) ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include /* for smp_call_function() prototype */ ++#include ++#include ++ ++#include ++ ++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;inext = 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 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++#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 ++#include ++#include ++ ++#include ++ ++#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 ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 + #endif ++#include + + 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 ++ ++/* 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<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 ++ ++#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 ++#include ++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 ++#include ++#include ++#include ++ ++#if defined(__KERNEL__) ++ ++#include ++ ++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 ++#include ++ ++#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 ++#include ++ ++#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 ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++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 ++ ++/******************************** 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 ++#include ++ ++/* 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 ++/* ++ * "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 ++ ++#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<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 ++#elif defined (LINUX) ++# include ++#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 ++#include ++ ++#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 ++#include ++#include ++ ++#if defined(DIGITAL_UNIX) ++# include ++#elif defined(LINUX) ++# include ++#elif defined(SOLARIS) ++# include ++#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 ++#include ++#include ++#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 ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#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 ++#include ++ ++/* ++ * 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 ++#include ++ ++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 ++ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MPSAS ++#include ++#endif ++ ++#if defined(LINUX) ++#include ++#elif defined(TRU64UNIX) ++#include ++#elif defined(SOLARIS) ++#include ++#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 ++ ++#if defined(MPSAS) ++#include ++#endif ++ ++#if defined(CONFIG_DEVFS_FS) ++#include ++#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 ++ ++/* 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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++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 ++ ++/* 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 ++ ++#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 ++/* ++ * "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 ++#include ++#include ++ ++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 ++#include ++#include ++#include /* 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 + #include + ++#include ++ + 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 ++#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 ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#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 ++#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 ++ ++#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 on AMD64 */ ++#include ++#include ++ ++#ifndef __cplusplus ++/* this header file has a parameter called "new" - great huh */ ++#include ++#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 ++#include ++ ++#if defined(SOLARIS) ++#include ++#endif ++ ++#if defined(DIGITAL_UNIX) ++#include ++#endif ++ ++#if defined(LINUX) ++#include ++#endif ++ ++#include ++ ++#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 ++#endif ++ ++#include ++#include ++ ++ ++/* 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 ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include /* Quadrics added */ ++ ++#include ++ ++#include ++#include ++#include ++ ++#if defined(LINUX_ALPHA) ++# include /* 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 */ ++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 ++ ++#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 ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++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 ++#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 ++#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 ++ ++/* ++ * 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 ++ * ++ * also defines the following: ++ * u_char, u_short, u_int, u_long, caddr_t ++ */ ++ ++#include ++ ++#if defined(SOLARIS) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(SOLARIS) && !defined(__KERNEL__) ++# include ++# include ++#endif ++ ++#if defined(DIGITAL_UNIX) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(DIGITAL_UNIX) && !defined(__KERNEL__) ++# include ++# include ++#endif ++ ++#if defined(LINUX) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(LINUX) && !defined(__KERNEL__) ++# include ++# include ++# include ++ ++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 ++# include ++#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 ++#endif ++ ++#include ++#include ++ ++#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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 ++#include ++#include ++#include ++#include ++ ++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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + +@@ -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 + #include + #include ++#include + #include + #include + +@@ -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 + #include + #include ++#include + + #include + #include +@@ -449,6 +450,7 @@ + } + + /* Nuke the page table entry. */ ++ coproc_invalidate_page(vma, address); + pte = vm_ptep_get_and_clear(vma, address, ptep); + flush_tlb_page(vma, address); + flush_cache_page(vma, address); diff --git a/lustre/kernel_patches/patches/qsnet-suse-2.6.patch b/lustre/kernel_patches/patches/qsnet-suse-2.6.patch new file mode 100644 index 0000000..d4d9be1 --- /dev/null +++ b/lustre/kernel_patches/patches/qsnet-suse-2.6.patch @@ -0,0 +1,94821 @@ +Index: linux-2.6.5/arch/i386/defconfig +=================================================================== +--- linux-2.6.5.orig/arch/i386/defconfig 2005-02-01 16:56:13.000000000 -0500 ++++ linux-2.6.5/arch/i386/defconfig 2005-05-11 12:10:12.362944128 -0400 +@@ -137,6 +137,8 @@ + CONFIG_EFI=y + CONFIG_BOOT_IOREMAP=y + CONFIG_REGPARM=y ++CONFIG_IOPROC=y ++CONFIG_PTRACK=y + + # + # Special options +Index: linux-2.6.5/arch/i386/Kconfig +=================================================================== +--- linux-2.6.5.orig/arch/i386/Kconfig 2005-05-11 12:10:10.831176992 -0400 ++++ linux-2.6.5/arch/i386/Kconfig 2005-05-11 12:10:12.363943976 -0400 +@@ -1024,6 +1024,9 @@ + a work-around for a number of buggy BIOSes. Switch this option on if + your computer crashes instead of powering off properly. + ++source "mm/Kconfig" ++source "kernel/Kconfig" ++ + endmenu + + source "arch/i386/kernel/cpu/cpufreq/Kconfig" +Index: linux-2.6.5/arch/i386/mm/hugetlbpage.c +=================================================================== +--- linux-2.6.5.orig/arch/i386/mm/hugetlbpage.c 2005-02-01 16:56:06.000000000 -0500 ++++ linux-2.6.5/arch/i386/mm/hugetlbpage.c 2005-05-11 12:10:12.364943824 -0400 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#undef ASSERT ++#include ++#include ++ ++ ++ ++#include ++#include ++ ++#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 ++#include ++ ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++#include ++ ++#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 ++#endif ++#include ++#include ++ ++/* ++ * 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 ++#include ++ ++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;railattached[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;iattached[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;railattached[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;railnode.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;railnode.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 ++ ++#else ++ ++#include ++#include ++#include ++ ++#endif ++ ++#include ++ ++ ++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 ++#include ++ ++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 ++#include ++ ++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 ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++ ++#include ++#include ++ ++#include ++ ++MODULE_AUTHOR("Quadrics Ltd."); ++MODULE_DESCRIPTION("Elan support module"); ++ ++MODULE_LICENSE("GPL"); ++ ++/* elanmod.c */ ++EXPORT_SYMBOL(elanmod_classify_cap); ++ ++/* bitmap.c */ ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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;islot_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 ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++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; jTrData[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 ++#include ++ ++caddr_t ++MiToName (int mi) ++{ ++ static char space[32]; ++ static struct { ++ int mi; ++ char *name; ++ } info[] = { ++#include ++ }; ++ 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef DIGITAL_UNIX ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++typedef xdrproc_t kxdrproc_t; ++#endif ++ ++#ifdef LINUX ++#include ++#include ++#include ++#include ++ ++#include ++#define SYS_NMLN __NEW_UTS_LEN ++#endif ++ ++#include ++ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++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, "\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, "")) ++ { ++ 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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;iStatus.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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++ ++#ifdef NO_ABI ++#include ++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 ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* 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; idev_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 ++#include ++#include ++ ++#include ++#include ++#ifdef CONFIG_MTRR ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23) ++typedef void irqreturn_t; ++#endif ++# define IRQ_NONE ++# define IRQ_HANDLED ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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; iucq_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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * ++ * 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, "\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, "\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, "")) ++ { ++ 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, ""); ++ else ++ { ++ if (i2c_read (dev, I2C_TEMP_ADDR, 2, values) < 0) ++ len = sprintf (page, ""); ++ 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; idev_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; iroutes[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; iroutes[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; idev_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; iroutes[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; iroutes[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; idev_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; iroutes[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; iroutes[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 ++ ++#include ++#include ++#include ++ ++/*================================================================================*/ ++/* 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 ++ ++#include ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++ ++#include ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++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 ++#include ++ ++/* ++ * 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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "debug.h" ++#include "cm.h" ++#include ++ ++#include ++ ++#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 (""); ++ 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 ++ ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "debug.h" ++#include "cm.h" ++#include ++ ++#include ++ ++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, "\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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++ ++#include "conf_linux.h" ++ ++#include ++#include ++#include ++#include ++ ++/* 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 ++ ++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 ++ ++#include ++ ++#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, ""); ++ 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 ++ ++#include ++ ++#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 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 ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++#include ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 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 ++ ++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 ++#include ++#include ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++/* 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 ++ ++#include ++#include ++#include ++ ++#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;fnFrags;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 ++#include ++#include ++#include ++ ++#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 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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++#include "debug.h" ++#include "conf_linux.h" ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "epcomms_elan4.h" ++ ++#include ++ ++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 ++ ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#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 */ ++#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 ++ ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#endif /* !defined(__ELAN3__) */ ++ ++#include ++ ++/* 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 ++#include ++ ++#include ++ ++#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 ++ ++#include ++#include ++#include ++ ++#if !defined(__elan4__) ++#include ++#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 ++#include ++ ++#include ++ ++#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 ++ ++/* 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 ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#include "debug.h" ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++ ++#include ++ ++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 ++ ++#include ++ ++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 ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++ ++#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 ++ ++#include ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan3.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++#include ++#include ++ ++#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 ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++#include ++ ++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 ++ ++#include ++#include ++#include ++ ++#include "cm.h" ++#include "debug.h" ++#include "conf_linux.h" ++#include ++#include ++#include ++ ++#include ++ ++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, "\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, "\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, ""); ++ 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, "\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 ++ ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++#include ++ ++/******************************** 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 ++#include ++ ++/****************************************************************************************/ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan3.h" ++#include "epcomms_elan3.h" ++#include "debug.h" ++ ++#include ++#include ++ ++/****************************************************************************************/ ++#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 ++#include ++ ++#include ++ ++#include "kcomm_vp.h" ++#include "kcomm_elan4.h" ++#include "debug.h" ++ ++#include ++#include ++ ++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 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 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 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 ++ ++#include ++ ++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 ++ ++#include ++ ++#include "kcomm_elan3.h" ++#include "debug.h" ++ ++#include ++ ++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 ++#include ++#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 ++#include ++#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 ++ ++#include "jtagdrv.h" ++#include ++ ++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 ++ ++/* 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 ++ ++#include ++#include ++ ++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 ++#include ++ ++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 ++#include ++#include ++ ++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 0 ) ++ { ++ int i; ++ for(i=0;i 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 ++ ++#if defined(LINUX_I386) ++ ++#include ++#include ++#include ++#include ++ ++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 ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include /* for smp_call_function() prototype */ ++#include ++#include ++ ++#include ++ ++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;inext = 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 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++ ++/* ++ * 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 ++ ++#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 ++#include ++#include ++ ++#include ++ ++#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 ++ ++#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 ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 + #endif + ++#include ++ + 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 ++ ++/* 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<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 ++ ++#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 ++#include ++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 ++#include ++#include ++#include ++ ++#if defined(__KERNEL__) ++ ++#include ++ ++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 ++#include ++ ++#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 ++#include ++ ++#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 ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 ++ ++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 ++ ++/******************************** 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 ++#include ++ ++/* 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 ++/* ++ * "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 ++ ++#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<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 ++#elif defined (LINUX) ++# include ++#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 ++#include ++ ++#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 ++#include ++#include ++ ++#if defined(DIGITAL_UNIX) ++# include ++#elif defined(LINUX) ++# include ++#elif defined(SOLARIS) ++# include ++#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 ++#include ++#include ++ ++#include ++ ++#if !defined(NO_COPROC) /* The older coproc kernel patch is applied */ ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#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 ++#include ++ ++/* ++ * 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 ++#include ++ ++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 ++ ++/* ++ * 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 ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_MPSAS ++#include ++#endif ++ ++#if defined(LINUX) ++#include ++#elif defined(TRU64UNIX) ++#include ++#elif defined(SOLARIS) ++#include ++#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 ++ ++#if !defined(NO_COPROC) /* The older coproc kernel patch is applied */ ++#include ++ ++#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 ++ ++#define IOPROC_PATCH_APPLIED 1 ++#endif ++ ++ ++#if defined(MPSAS) ++#include ++#endif ++ ++#if defined(CONFIG_DEVFS_FS) ++#include ++#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 ++ ++/* 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 ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++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 ++ ++/* 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 ++ ++#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 ++/* ++ * "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 ++#include ++#include ++ ++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 + #include ++#include + + #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 ++#include ++ ++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 ++#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 ++#include ++ ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#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 ++#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 ++ ++#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 on AMD64 */ ++#include ++#include ++ ++#ifndef __cplusplus ++/* this header file has a parameter called "new" - great huh */ ++#include ++#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 ++#include ++ ++#if defined(SOLARIS) ++#include ++#endif ++ ++#if defined(DIGITAL_UNIX) ++#include ++#endif ++ ++#if defined(LINUX) ++#include ++#endif ++ ++#include ++ ++#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 ++#endif ++ ++#include ++#include ++ ++ ++/* 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 ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#if defined(LINUX_ALPHA) ++# include /* 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 */ ++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 ++ ++#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 ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++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 ++#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 ++#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 ++ ++/* ++ * 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 ++ * ++ * also defines the following: ++ * u_char, u_short, u_int, u_long, caddr_t ++ */ ++ ++#include ++ ++#if defined(SOLARIS) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(SOLARIS) && !defined(__KERNEL__) ++# include ++# include ++#endif ++ ++#if defined(DIGITAL_UNIX) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(DIGITAL_UNIX) && !defined(__KERNEL__) ++# include ++# include ++#endif ++ ++#if defined(LINUX) && defined(__KERNEL__) ++# include ++#endif ++ ++#if defined(LINUX) && !defined(__KERNEL__) ++# include ++# include ++# include ++ ++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 ++# include ++#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 ++#endif ++ ++#include ++#include ++ ++#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 + #include + #include ++#include + #include + #include + +@@ -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 ++ + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++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 + #include + #include ++#include + + #include + #include +@@ -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 ++#include ++ ++#include ++#include ++ ++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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + #include +@@ -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 + #include ++#include + #include + #include + #include +@@ -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 + #include ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + + #include + #include +@@ -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 + #include + #include ++#include + #include + + 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); + diff --git a/lustre/kernel_patches/patches/small_scatterlist-2.4.21-rhel.patch b/lustre/kernel_patches/patches/small_scatterlist-2.4.21-rhel.patch index 2dae026..381d490 100644 --- a/lustre/kernel_patches/patches/small_scatterlist-2.4.21-rhel.patch +++ b/lustre/kernel_patches/patches/small_scatterlist-2.4.21-rhel.patch @@ -1,7 +1,7 @@ Index: linux-2.4.21/crypto/cipher.c =================================================================== ---- linux-2.4.21.orig/crypto/cipher.c 2004-12-21 13:51:10.000000000 -0500 -+++ linux-2.4.21/crypto/cipher.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/crypto/cipher.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/crypto/cipher.c 2005-06-01 23:07:51.067582960 -0400 @@ -88,12 +88,21 @@ walk->sg = sg; @@ -26,8 +26,8 @@ Index: linux-2.4.21/crypto/cipher.c static void scatterwalk_map(struct scatter_walk *walk, int out) Index: linux-2.4.21/crypto/digest.c =================================================================== ---- linux-2.4.21.orig/crypto/digest.c 2004-12-21 13:51:10.000000000 -0500 -+++ linux-2.4.21/crypto/digest.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/crypto/digest.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/crypto/digest.c 2005-06-01 23:07:51.068582808 -0400 @@ -29,7 +29,11 @@ unsigned int i; @@ -54,8 +54,8 @@ Index: linux-2.4.21/crypto/digest.c crypto_kunmap(p, 0); Index: linux-2.4.21/crypto/hmac.c =================================================================== ---- linux-2.4.21.orig/crypto/hmac.c 2004-12-21 13:51:10.000000000 -0500 -+++ linux-2.4.21/crypto/hmac.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/crypto/hmac.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/crypto/hmac.c 2005-06-01 23:07:51.068582808 -0400 @@ -25,8 +25,14 @@ { struct scatterlist tmp; @@ -116,8 +116,8 @@ Index: linux-2.4.21/crypto/hmac.c crypto_digest_update(tfm, &tmp, 1); Index: linux-2.4.21/crypto/tcrypt.c =================================================================== ---- linux-2.4.21.orig/crypto/tcrypt.c 2004-12-21 13:51:10.000000000 -0500 -+++ linux-2.4.21/crypto/tcrypt.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/crypto/tcrypt.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/crypto/tcrypt.c 2005-06-01 23:07:51.071582352 -0400 @@ -24,6 +24,15 @@ #include #include "tcrypt.h" @@ -1189,8 +1189,8 @@ Index: linux-2.4.21/crypto/tcrypt.c printk("%s\n", memcmp(q, c5_tv[i].ciphertext, Index: linux-2.4.21/drivers/ide/ide-dma.c =================================================================== ---- linux-2.4.21.orig/drivers/ide/ide-dma.c 2004-12-21 13:51:31.000000000 -0500 -+++ linux-2.4.21/drivers/ide/ide-dma.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/ide/ide-dma.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/ide/ide-dma.c 2005-06-01 23:07:51.072582200 -0400 @@ -280,14 +280,25 @@ memset(&sg[nents], 0, sizeof(*sg)); @@ -1244,8 +1244,8 @@ Index: linux-2.4.21/drivers/ide/ide-dma.c #else Index: linux-2.4.21/drivers/scsi/dpt_i2o.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/dpt_i2o.c 2004-12-21 13:51:20.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/dpt_i2o.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/dpt_i2o.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/dpt_i2o.c 2005-06-01 23:07:51.074581896 -0400 @@ -2152,7 +2152,13 @@ for(i = 0 ; i < cmd->use_sg; i++) { *mptr++ = direction|0x10000000|sg->length; @@ -1263,7 +1263,7 @@ Index: linux-2.4.21/drivers/scsi/dpt_i2o.c Index: linux-2.4.21/drivers/scsi/scsi_debug.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/scsi_debug.c 2002-11-28 18:53:14.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/scsi_debug.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/scsi_debug.c 2005-06-01 23:07:51.075581744 -0400 @@ -186,7 +186,13 @@ struct scatterlist *sgpnt = (struct scatterlist *) SCpnt->request_buffer; @@ -1336,8 +1336,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_debug.c } else if (nbytes > 0) Index: linux-2.4.21/drivers/scsi/scsi_lib.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/scsi_lib.c 2004-12-21 13:51:31.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/scsi_lib.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/scsi_lib.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/scsi_lib.c 2005-06-01 23:07:51.076581592 -0400 @@ -554,7 +554,13 @@ if (bbpnt) { for (i = 0; i < SCpnt->use_sg; i++) { @@ -1378,8 +1378,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_lib.c } Index: linux-2.4.21/drivers/scsi/scsi_merge.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/scsi_merge.c 2004-12-21 13:51:06.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/scsi_merge.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/scsi_merge.c 2005-06-01 22:51:45.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/scsi_merge.c 2005-06-01 23:07:51.077581440 -0400 @@ -144,11 +144,21 @@ */ for(jj=0; jj < SCpnt->use_sg; jj++) @@ -1500,8 +1500,8 @@ Index: linux-2.4.21/drivers/scsi/scsi_merge.c Index: linux-2.4.21/drivers/scsi/sg.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/sg.c 2004-12-21 13:51:21.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/sg.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/sg.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/sg.c 2005-06-01 23:07:51.080580984 -0400 @@ -1077,7 +1077,11 @@ for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) { @@ -1708,8 +1708,8 @@ Index: linux-2.4.21/drivers/scsi/sg.c sfp->save_scat_len = num; Index: linux-2.4.21/drivers/scsi/sr.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/sr.c 2004-12-21 13:51:21.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/sr.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/sr.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/sr.c 2005-06-01 23:07:51.080580984 -0400 @@ -343,7 +343,12 @@ i = 0; @@ -1752,7 +1752,7 @@ Index: linux-2.4.21/drivers/scsi/sr.c Index: linux-2.4.21/drivers/scsi/sim710.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/sim710.c 2002-08-02 20:39:44.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/sim710.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/sim710.c 2005-06-01 23:07:51.082580680 -0400 @@ -1164,7 +1164,11 @@ for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; i++) { @@ -1767,8 +1767,8 @@ Index: linux-2.4.21/drivers/scsi/sim710.c u32 cnt = cmd->use_sg ? Index: linux-2.4.21/drivers/scsi/advansys.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/advansys.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/advansys.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/advansys.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/advansys.c 2005-06-01 23:07:51.097578400 -0400 @@ -6803,7 +6803,11 @@ slp = (struct scatterlist *) scp->request_buffer; for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) { @@ -1796,7 +1796,7 @@ Index: linux-2.4.21/drivers/scsi/advansys.c Index: linux-2.4.21/drivers/scsi/pci2000.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/pci2000.c 2001-11-09 17:05:06.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/pci2000.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/pci2000.c 2005-06-01 23:07:51.098578248 -0400 @@ -513,7 +513,11 @@ if ( SCpnt->use_sg ) @@ -1824,7 +1824,7 @@ Index: linux-2.4.21/drivers/scsi/pci2000.c Index: linux-2.4.21/drivers/scsi/pci2220i.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/pci2220i.c 2001-11-09 17:05:06.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/pci2220i.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/pci2220i.c 2005-06-01 23:07:51.100577944 -0400 @@ -463,7 +463,11 @@ { if ( padapter->nextSg < padapter->SCpnt->use_sg ) @@ -1852,7 +1852,7 @@ Index: linux-2.4.21/drivers/scsi/pci2220i.c Index: linux-2.4.21/drivers/scsi/BusLogic.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/BusLogic.c 2001-12-21 12:41:55.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/BusLogic.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/BusLogic.c 2005-06-01 23:07:51.104577336 -0400 @@ -3402,7 +3402,12 @@ CCB->ScatterGatherList[Segment].SegmentByteCount = ScatterList[Segment].length; @@ -1869,7 +1869,7 @@ Index: linux-2.4.21/drivers/scsi/BusLogic.c Index: linux-2.4.21/drivers/scsi/ultrastor.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/ultrastor.c 2002-08-02 20:39:44.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/ultrastor.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/ultrastor.c 2005-06-01 23:07:51.105577184 -0400 @@ -666,7 +666,11 @@ sl = (struct scatterlist *) SCpnt->request_buffer; max = SCpnt->use_sg; @@ -1885,7 +1885,7 @@ Index: linux-2.4.21/drivers/scsi/ultrastor.c Index: linux-2.4.21/drivers/scsi/aha152x.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/aha152x.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/aha152x.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/aha152x.c 2005-06-01 23:07:51.108576728 -0400 @@ -603,7 +603,11 @@ #define SCSEM(SCpnt) SCDATA(SCpnt)->sem @@ -1901,7 +1901,7 @@ Index: linux-2.4.21/drivers/scsi/aha152x.c Index: linux-2.4.21/drivers/scsi/aha1542.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/aha1542.c 2001-10-12 18:35:53.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/aha1542.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/aha1542.c 2005-06-01 23:07:51.109576576 -0400 @@ -69,8 +69,13 @@ { printk(KERN_CRIT "sgpnt[%d:%d] addr %p/0x%lx length %d\n", @@ -1957,7 +1957,7 @@ Index: linux-2.4.21/drivers/scsi/aha1542.c Index: linux-2.4.21/drivers/scsi/aha1740.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/aha1740.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/aha1740.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/aha1740.c 2005-06-01 23:07:51.110576424 -0400 @@ -397,7 +397,11 @@ for(i=0; iuse_sg; i++) { @@ -1972,8 +1972,8 @@ Index: linux-2.4.21/drivers/scsi/aha1740.c host->ecb[ecbno].dataptr = virt_to_bus(cptr); Index: linux-2.4.21/drivers/scsi/aic7xxx_old.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/aic7xxx_old.c 2004-12-21 13:51:20.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/aic7xxx_old.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/aic7xxx_old.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/aic7xxx_old.c 2005-06-01 23:07:51.118575208 -0400 @@ -2845,7 +2845,11 @@ struct scatterlist *sg; @@ -1989,7 +1989,7 @@ Index: linux-2.4.21/drivers/scsi/aic7xxx_old.c Index: linux-2.4.21/drivers/scsi/fdomain.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/fdomain.c 2002-11-28 18:53:14.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/fdomain.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/fdomain.c 2005-06-01 23:07:51.120574904 -0400 @@ -1564,7 +1564,11 @@ if (current_SC->SCp.buffers_residual) { --current_SC->SCp.buffers_residual; @@ -2029,7 +2029,7 @@ Index: linux-2.4.21/drivers/scsi/fdomain.c Index: linux-2.4.21/drivers/scsi/in2000.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/in2000.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/in2000.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/in2000.c 2005-06-01 23:07:51.122574600 -0400 @@ -355,7 +355,11 @@ if (cmd->use_sg) { cmd->SCp.buffer = (struct scatterlist *)cmd->buffer; @@ -2057,7 +2057,7 @@ Index: linux-2.4.21/drivers/scsi/in2000.c Index: linux-2.4.21/drivers/scsi/NCR5380.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/NCR5380.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/NCR5380.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/NCR5380.c 2005-06-01 23:07:51.124574296 -0400 @@ -337,7 +337,11 @@ if (cmd->use_sg) { cmd->SCp.buffer = (struct scatterlist *) cmd->buffer; @@ -2085,7 +2085,7 @@ Index: linux-2.4.21/drivers/scsi/NCR5380.c Index: linux-2.4.21/drivers/scsi/NCR53c406a.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/NCR53c406a.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/NCR53c406a.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/NCR53c406a.c 2005-06-01 23:07:51.125574144 -0400 @@ -895,7 +895,11 @@ sgcount = current_SC->use_sg; sglist = current_SC->request_buffer; @@ -2113,7 +2113,7 @@ Index: linux-2.4.21/drivers/scsi/NCR53c406a.c Index: linux-2.4.21/drivers/scsi/sym53c416.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/sym53c416.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/sym53c416.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/sym53c416.c 2005-06-01 23:07:51.126573992 -0400 @@ -448,7 +448,11 @@ sglist = current_command->request_buffer; while(sgcount--) @@ -2141,7 +2141,7 @@ Index: linux-2.4.21/drivers/scsi/sym53c416.c Index: linux-2.4.21/drivers/scsi/qlogicfas.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/qlogicfas.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/qlogicfas.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/qlogicfas.c 2005-06-01 23:07:51.126573992 -0400 @@ -393,7 +393,11 @@ REG0; return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16); @@ -2157,7 +2157,7 @@ Index: linux-2.4.21/drivers/scsi/qlogicfas.c Index: linux-2.4.21/drivers/scsi/qla1280.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/qla1280.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/qla1280.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/qla1280.c 2005-06-01 23:07:51.131573232 -0400 @@ -3985,8 +3985,13 @@ { DEBUG(sprintf(debug_buff,"SG Segment ap=0x%p, len=0x%x\n\r",sg->address,sg->length)); @@ -2212,8 +2212,8 @@ Index: linux-2.4.21/drivers/scsi/qla1280.c } Index: linux-2.4.21/drivers/scsi/seagate.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/seagate.c 2004-12-21 13:51:00.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/seagate.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/seagate.c 2005-06-01 22:51:40.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/seagate.c 2005-06-01 23:07:51.133572928 -0400 @@ -1070,7 +1070,11 @@ buffer = (struct scatterlist *) SCint->buffer; @@ -2254,7 +2254,7 @@ Index: linux-2.4.21/drivers/scsi/seagate.c Index: linux-2.4.21/drivers/scsi/53c7,8xx.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/53c7,8xx.c 2002-02-25 14:38:04.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/53c7,8xx.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/53c7,8xx.c 2005-06-01 23:07:51.137572320 -0400 @@ -3787,7 +3787,11 @@ for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4, cmd_dataout += 4, ++i) { @@ -2295,8 +2295,8 @@ Index: linux-2.4.21/drivers/scsi/53c7,8xx.c offset = ptr - (char *) (cmd->request_buffer); Index: linux-2.4.21/drivers/scsi/eata_dma.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/eata_dma.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/eata_dma.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/eata_dma.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/eata_dma.c 2005-06-01 23:07:51.138572168 -0400 @@ -571,7 +571,11 @@ ccb->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list)); sl=(struct scatterlist *)cmd->request_buffer; @@ -2312,7 +2312,7 @@ Index: linux-2.4.21/drivers/scsi/eata_dma.c Index: linux-2.4.21/drivers/scsi/eata_pio.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/eata_pio.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/eata_pio.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/eata_pio.c 2005-06-01 23:07:51.139572016 -0400 @@ -99,7 +99,11 @@ else { @@ -2340,7 +2340,7 @@ Index: linux-2.4.21/drivers/scsi/eata_pio.c Index: linux-2.4.21/drivers/scsi/wd7000.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/wd7000.c 2001-09-30 15:26:08.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/wd7000.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/wd7000.c 2005-06-01 23:07:51.140571864 -0400 @@ -1189,7 +1189,11 @@ any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb)); @@ -2356,7 +2356,7 @@ Index: linux-2.4.21/drivers/scsi/wd7000.c Index: linux-2.4.21/drivers/scsi/scsiiom.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/scsiiom.c 2000-12-31 14:06:00.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/scsiiom.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/scsiiom.c 2005-06-01 23:07:51.142571560 -0400 @@ -379,7 +379,11 @@ pSRB->pSegmentList++; psgl = pSRB->pSegmentList; @@ -2474,7 +2474,7 @@ Index: linux-2.4.21/drivers/scsi/scsiiom.c Index: linux-2.4.21/drivers/scsi/tmscsim.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/tmscsim.c 2001-12-21 12:41:55.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/tmscsim.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/tmscsim.c 2005-06-01 23:07:51.144571256 -0400 @@ -1146,7 +1146,11 @@ { pSRB->SGcount = 1; @@ -2490,7 +2490,7 @@ Index: linux-2.4.21/drivers/scsi/tmscsim.c Index: linux-2.4.21/drivers/scsi/AM53C974.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/AM53C974.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/AM53C974.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/AM53C974.c 2005-06-01 23:07:51.146570952 -0400 @@ -842,7 +842,11 @@ if (cmd->use_sg) { cmd->SCp.buffer = (struct scatterlist *) cmd->buffer; @@ -2517,8 +2517,8 @@ Index: linux-2.4.21/drivers/scsi/AM53C974.c if (cmd->SCp.this_residual) { Index: linux-2.4.21/drivers/scsi/megaraid2.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/megaraid2.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/megaraid2.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/megaraid2.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/megaraid2.c 2005-06-01 23:07:51.149570496 -0400 @@ -2180,7 +2180,11 @@ if( cmd->use_sg ) { sgl = (struct scatterlist *) @@ -2533,8 +2533,8 @@ Index: linux-2.4.21/drivers/scsi/megaraid2.c c = *(u8 *)cmd->request_buffer; Index: linux-2.4.21/drivers/scsi/megaraid.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/megaraid.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/megaraid.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/megaraid.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/megaraid.c 2005-06-01 23:07:51.152570040 -0400 @@ -1201,8 +1201,13 @@ case READ_CAPACITY: if ( SCpnt->use_sg ) { @@ -2565,7 +2565,7 @@ Index: linux-2.4.21/drivers/scsi/megaraid.c Index: linux-2.4.21/drivers/scsi/atp870u.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/atp870u.c 2002-08-02 20:39:44.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/atp870u.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/atp870u.c 2005-06-01 23:07:51.154569736 -0400 @@ -804,7 +804,11 @@ sgpnt = (struct scatterlist *) workrequ->request_buffer; i = 0; @@ -2581,7 +2581,7 @@ Index: linux-2.4.21/drivers/scsi/atp870u.c Index: linux-2.4.21/drivers/scsi/gdth.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/gdth.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/gdth.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/gdth.c 2005-06-01 23:07:51.158569128 -0400 @@ -2662,7 +2662,11 @@ if (cpsum+cpnow > cpcount) cpnow = cpcount - cpsum; @@ -2621,7 +2621,7 @@ Index: linux-2.4.21/drivers/scsi/gdth.c Index: linux-2.4.21/drivers/scsi/ini9100u.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/ini9100u.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/ini9100u.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/ini9100u.c 2005-06-01 23:07:51.159568976 -0400 @@ -489,7 +489,11 @@ if (SCpnt->use_sg) { pSrbSG = (struct scatterlist *) SCpnt->request_buffer; @@ -2649,7 +2649,7 @@ Index: linux-2.4.21/drivers/scsi/ini9100u.c Index: linux-2.4.21/drivers/scsi/inia100.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/inia100.c 2001-09-30 15:26:07.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/inia100.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/inia100.c 2005-06-01 23:07:51.159568976 -0400 @@ -494,7 +494,11 @@ pSCB->SCB_SGLen = (U32) (SCpnt->use_sg * 8); pSrbSG = (struct scatterlist *) SCpnt->request_buffer; @@ -2664,8 +2664,8 @@ Index: linux-2.4.21/drivers/scsi/inia100.c } Index: linux-2.4.21/drivers/scsi/ide-scsi.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/ide-scsi.c 2004-12-21 13:51:31.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/ide-scsi.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/ide-scsi.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/ide-scsi.c 2005-06-01 23:07:51.160568824 -0400 @@ -154,7 +154,11 @@ return; } @@ -2720,8 +2720,8 @@ Index: linux-2.4.21/drivers/scsi/ide-scsi.c sg++; Index: linux-2.4.21/drivers/scsi/ppa.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/ppa.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/ppa.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/ppa.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/ppa.c 2005-06-01 23:07:51.161568672 -0400 @@ -740,7 +740,11 @@ if (cmd->SCp.buffers_residual--) { cmd->SCp.buffer++; @@ -2748,8 +2748,8 @@ Index: linux-2.4.21/drivers/scsi/ppa.c cmd->SCp.buffer = NULL; Index: linux-2.4.21/drivers/scsi/imm.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/imm.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/imm.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/imm.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/imm.c 2005-06-01 23:07:51.162568520 -0400 @@ -834,7 +834,11 @@ if (cmd->SCp.buffers_residual--) { cmd->SCp.buffer++; @@ -2776,8 +2776,8 @@ Index: linux-2.4.21/drivers/scsi/imm.c cmd->SCp.buffer = NULL; Index: linux-2.4.21/drivers/scsi/st.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/st.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/st.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/st.c 2005-06-01 22:52:04.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/st.c 2005-06-01 23:07:51.164568216 -0400 @@ -3409,6 +3409,12 @@ } @@ -2924,8 +2924,8 @@ Index: linux-2.4.21/drivers/scsi/st.c src_offset += count; Index: linux-2.4.21/drivers/scsi/osst.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/osst.c 2004-12-21 13:51:30.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/osst.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/osst.c 2005-06-01 22:52:04.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/osst.c 2005-06-01 23:07:51.169567456 -0400 @@ -477,7 +477,11 @@ if (STp->raw) { if (STp->buffer->syscall_result) { @@ -3111,7 +3111,7 @@ Index: linux-2.4.21/drivers/scsi/osst.c Index: linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c =================================================================== --- linux-2.4.21.orig/drivers/scsi/pcmcia/nsp_cs.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c 2005-06-01 23:07:51.170567304 -0400 @@ -201,7 +201,11 @@ SCp.phase : current state of the command */ if (SCpnt->use_sg) { @@ -3150,8 +3150,8 @@ Index: linux-2.4.21/drivers/scsi/pcmcia/nsp_cs.c Index: linux-2.4.21/drivers/scsi/ips.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/ips.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/ips.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/scsi/ips.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/ips.c 2005-06-01 23:07:51.175566544 -0400 @@ -217,7 +217,11 @@ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) #include @@ -3166,9 +3166,9 @@ Index: linux-2.4.21/drivers/scsi/ips.c #ifndef __devexit_p Index: linux-2.4.21/drivers/scsi/libata-core.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/libata-core.c 2004-12-21 13:51:26.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/libata-core.c 2004-12-21 13:58:10.000000000 -0500 -@@ -1915,8 +1915,13 @@ +--- linux-2.4.21.orig/drivers/scsi/libata-core.c 2005-06-01 22:51:58.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/libata-core.c 2005-06-01 23:36:21.244596536 -0400 +@@ -2068,8 +2068,13 @@ qc->buf_virt = buf; sg = qc->sg; @@ -3180,29 +3180,41 @@ Index: linux-2.4.21/drivers/scsi/libata-core.c sg->offset = (unsigned long) buf & ~PAGE_MASK; +#endif /* !SMALL_SCATTERLIST */ sg_dma_len(sg) = buflen; + } - /* WARN_ON(buflen > PAGE_SIZE); */ -@@ -2143,9 +2148,15 @@ +@@ -2297,8 +2302,13 @@ if (qc->cursect == (qc->nsect - 1)) ap->pio_task_state = PIO_ST_LAST; +#if SMALL_SCATTERLIST + page = sg[qc->cursg].u.page.page; -+ buf = kmap(page) + -+ sg[qc->cursg].u.page.offset + (qc->cursg_ofs * ATA_SECT_SIZE); ++ offset = sg[qc->cursg].u.page.offset + qc->cursg_ofs * ATA_SECT_SIZE; +#else page = sg[qc->cursg].page; - buf = kmap(page) + - sg[qc->cursg].offset + (qc->cursg_ofs * ATA_SECT_SIZE); + offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE; +#endif /* SMALL_SCATTERLIST */ - qc->cursect++; - qc->cursg_ofs++; + /* get the current page and offset */ + page = nth_page(page, (offset >> PAGE_SHIFT)); +@@ -2339,8 +2349,13 @@ + sg = &qc->sg[qc->cursg]; + + next_page: ++#if SMALL_SCATTERLIST ++ page = sg->u.page.page; ++ offset = sg->u.page.offset + qc->cursg_ofs; ++#else + page = sg->page; + offset = sg->offset + qc->cursg_ofs; ++#endif + + /* get the current page and offset */ + page = nth_page(page, (offset >> PAGE_SHIFT)); Index: linux-2.4.21/drivers/scsi/libata-scsi.c =================================================================== ---- linux-2.4.21.orig/drivers/scsi/libata-scsi.c 2004-12-21 13:51:26.000000000 -0500 -+++ linux-2.4.21/drivers/scsi/libata-scsi.c 2004-12-21 13:58:10.000000000 -0500 -@@ -686,7 +686,11 @@ +--- linux-2.4.21.orig/drivers/scsi/libata-scsi.c 2005-06-01 22:51:58.000000000 -0400 ++++ linux-2.4.21/drivers/scsi/libata-scsi.c 2005-06-01 23:12:15.112442040 -0400 +@@ -689,7 +689,11 @@ struct scatterlist *sg; sg = (struct scatterlist *) cmd->request_buffer; @@ -3214,22 +3226,22 @@ Index: linux-2.4.21/drivers/scsi/libata-scsi.c buflen = sg->length; } else { buf = cmd->request_buffer; -@@ -714,7 +718,11 @@ +@@ -717,7 +721,11 @@ struct scatterlist *sg; sg = (struct scatterlist *) cmd->request_buffer; +#if SMALL_SCATTERLIST -+ kunmap_atomic(sg->u.page.page, KM_USER0); ++ kunmap_atomic(buf - sg->u.page.offset, KM_USER0); +#else - kunmap_atomic(sg->page, KM_USER0); + kunmap_atomic(buf - sg->offset, KM_USER0); +#endif /* !SMALL_SCATTERLIST */ } } Index: linux-2.4.21/drivers/block/cpqarray.c =================================================================== ---- linux-2.4.21.orig/drivers/block/cpqarray.c 2004-12-21 13:51:20.000000000 -0500 -+++ linux-2.4.21/drivers/block/cpqarray.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/block/cpqarray.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/block/cpqarray.c 2005-06-01 23:07:51.183565328 -0400 @@ -1003,9 +1003,14 @@ } else { if (seg == SG_MAX) @@ -3271,8 +3283,8 @@ Index: linux-2.4.21/drivers/block/cpqarray.c c->req.hdr.sg_cnt = seg; Index: linux-2.4.21/drivers/block/cciss.c =================================================================== ---- linux-2.4.21.orig/drivers/block/cciss.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/block/cciss.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/block/cciss.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/block/cciss.c 2005-06-01 23:07:51.185565024 -0400 @@ -2491,9 +2491,14 @@ } else { if (seg == MAXSGENTRIES) @@ -3310,8 +3322,8 @@ Index: linux-2.4.21/drivers/block/cciss.c c->SG[i].Ext = 0; /* we are not chaining */ Index: linux-2.4.21/drivers/block/sx8.c =================================================================== ---- linux-2.4.21.orig/drivers/block/sx8.c 2004-12-21 13:51:26.000000000 -0500 -+++ linux-2.4.21/drivers/block/sx8.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/block/sx8.c 2005-06-01 22:51:58.000000000 -0400 ++++ linux-2.4.21/drivers/block/sx8.c 2005-06-01 23:07:51.186564872 -0400 @@ -1103,9 +1103,15 @@ } else { if (unlikely(n_elem == CARM_MAX_REQ_SG)) @@ -3330,8 +3342,8 @@ Index: linux-2.4.21/drivers/block/sx8.c } Index: linux-2.4.21/drivers/ieee1394/dma.c =================================================================== ---- linux-2.4.21.orig/drivers/ieee1394/dma.c 2003-06-13 10:51:34.000000000 -0400 -+++ linux-2.4.21/drivers/ieee1394/dma.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/ieee1394/dma.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/drivers/ieee1394/dma.c 2005-06-01 23:07:51.187564720 -0400 @@ -97,8 +97,12 @@ /* fill scatter/gather list with pages */ for(i = 0; i < dma->n_pages; i++) { @@ -3349,7 +3361,7 @@ Index: linux-2.4.21/drivers/ieee1394/dma.c Index: linux-2.4.21/drivers/ieee1394/sbp2.c =================================================================== --- linux-2.4.21.orig/drivers/ieee1394/sbp2.c 2003-06-13 10:51:34.000000000 -0400 -+++ linux-2.4.21/drivers/ieee1394/sbp2.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/ieee1394/sbp2.c 2005-06-01 23:07:51.189564416 -0400 @@ -2036,11 +2036,19 @@ command->dma_dir = dma_dir; command->dma_size = sgpnt[0].length; @@ -3373,7 +3385,7 @@ Index: linux-2.4.21/drivers/ieee1394/sbp2.c Index: linux-2.4.21/drivers/message/i2o/i2o_scsi.c =================================================================== --- linux-2.4.21.orig/drivers/message/i2o/i2o_scsi.c 2002-08-02 20:39:44.000000000 -0400 -+++ linux-2.4.21/drivers/message/i2o/i2o_scsi.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/message/i2o/i2o_scsi.c 2005-06-01 23:07:51.190564264 -0400 @@ -693,7 +693,11 @@ { *mptr++=direction|0x10000000|sg->length; @@ -3401,7 +3413,7 @@ Index: linux-2.4.21/drivers/message/i2o/i2o_scsi.c Index: linux-2.4.21/drivers/net/fc/iph5526.c =================================================================== --- linux-2.4.21.orig/drivers/net/fc/iph5526.c 2003-06-13 10:51:34.000000000 -0400 -+++ linux-2.4.21/drivers/net/fc/iph5526.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/net/fc/iph5526.c 2005-06-01 23:07:51.193563808 -0400 @@ -4249,7 +4249,11 @@ if (Cmnd->use_sg) { int count = 0, j; @@ -3450,8 +3462,8 @@ Index: linux-2.4.21/drivers/net/fc/iph5526.c update_EDB_indx(fi); Index: linux-2.4.21/drivers/net/wireless/airo.c =================================================================== ---- linux-2.4.21.orig/drivers/net/wireless/airo.c 2004-12-21 13:51:20.000000000 -0500 -+++ linux-2.4.21/drivers/net/wireless/airo.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/net/wireless/airo.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/drivers/net/wireless/airo.c 2005-06-01 23:07:51.198563048 -0400 @@ -1584,11 +1584,20 @@ aes_counter[12] = (u8)(counter >> 24); counter++; @@ -3476,7 +3488,7 @@ Index: linux-2.4.21/drivers/net/wireless/airo.c Index: linux-2.4.21/drivers/usb/microtek.c =================================================================== --- linux-2.4.21.orig/drivers/usb/microtek.c 2002-11-28 18:53:14.000000000 -0500 -+++ linux-2.4.21/drivers/usb/microtek.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/microtek.c 2005-06-01 23:07:51.199562896 -0400 @@ -623,7 +623,11 @@ context->fragment++; mts_int_submit_urb(transfer, @@ -3504,7 +3516,7 @@ Index: linux-2.4.21/drivers/usb/microtek.c Index: linux-2.4.21/drivers/usb/hpusbscsi.c =================================================================== --- linux-2.4.21.orig/drivers/usb/hpusbscsi.c 2003-06-13 10:51:36.000000000 -0400 -+++ linux-2.4.21/drivers/usb/hpusbscsi.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/hpusbscsi.c 2005-06-01 23:07:51.199562896 -0400 @@ -576,6 +576,17 @@ hpusbscsi->state = HP_STATE_WORKING; PDEBUG(2, "state= %s", states[hpusbscsi->state]); @@ -3534,7 +3546,7 @@ Index: linux-2.4.21/drivers/usb/hpusbscsi.c Index: linux-2.4.21/drivers/usb/storage/protocol.c =================================================================== --- linux-2.4.21.orig/drivers/usb/storage/protocol.c 2002-08-02 20:39:45.000000000 -0400 -+++ linux-2.4.21/drivers/usb/storage/protocol.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/storage/protocol.c 2005-06-01 23:07:51.200562744 -0400 @@ -72,7 +72,11 @@ struct scatterlist *sg; @@ -3549,8 +3561,8 @@ Index: linux-2.4.21/drivers/usb/storage/protocol.c Index: linux-2.4.21/drivers/usb/storage/transport.c =================================================================== ---- linux-2.4.21.orig/drivers/usb/storage/transport.c 2004-12-21 13:51:15.000000000 -0500 -+++ linux-2.4.21/drivers/usb/storage/transport.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/usb/storage/transport.c 2005-06-01 22:51:52.000000000 -0400 ++++ linux-2.4.21/drivers/usb/storage/transport.c 2005-06-01 23:07:51.201562592 -0400 @@ -592,11 +592,20 @@ if (transfer_amount - total_transferred >= sg[i].length) { @@ -3575,8 +3587,8 @@ Index: linux-2.4.21/drivers/usb/storage/transport.c /* if we get an error, end the loop here */ Index: linux-2.4.21/drivers/usb/storage/usb.c =================================================================== ---- linux-2.4.21.orig/drivers/usb/storage/usb.c 2004-12-21 13:51:15.000000000 -0500 -+++ linux-2.4.21/drivers/usb/storage/usb.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/usb/storage/usb.c 2005-06-01 22:51:52.000000000 -0400 ++++ linux-2.4.21/drivers/usb/storage/usb.c 2005-06-01 23:07:51.202562440 -0400 @@ -291,13 +291,21 @@ if (us->srb->use_sg) { sg = (struct scatterlist *)us->srb->request_buffer; @@ -3602,7 +3614,7 @@ Index: linux-2.4.21/drivers/usb/storage/usb.c Index: linux-2.4.21/drivers/usb/storage/shuttle_usbat.c =================================================================== --- linux-2.4.21.orig/drivers/usb/storage/shuttle_usbat.c 2003-06-13 10:51:37.000000000 -0400 -+++ linux-2.4.21/drivers/usb/storage/shuttle_usbat.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/storage/shuttle_usbat.c 2005-06-01 23:07:51.202562440 -0400 @@ -217,7 +217,11 @@ sg = (struct scatterlist *)data; for (i=0; i= sg[i].length) { @@ -3861,7 +3873,7 @@ Index: linux-2.4.21/drivers/usb/storage/freecom.c Index: linux-2.4.21/drivers/usb/storage/isd200.c =================================================================== --- linux-2.4.21.orig/drivers/usb/storage/isd200.c 2003-06-13 10:51:37.000000000 -0400 -+++ linux-2.4.21/drivers/usb/storage/isd200.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/storage/isd200.c 2005-06-01 23:07:51.206561832 -0400 @@ -494,13 +494,21 @@ sg[i].length) { result = isd200_transfer_partial(us, @@ -3906,7 +3918,7 @@ Index: linux-2.4.21/drivers/usb/storage/isd200.c Index: linux-2.4.21/drivers/usb/storage/datafab.c =================================================================== --- linux-2.4.21.orig/drivers/usb/storage/datafab.c 2003-06-13 10:51:37.000000000 -0400 -+++ linux-2.4.21/drivers/usb/storage/datafab.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/storage/datafab.c 2005-06-01 23:07:51.207561680 -0400 @@ -265,18 +265,30 @@ while (sg_idx < use_sg && transferred < len) { if (len - transferred >= sg[sg_idx].length - current_sg_offset) { @@ -3972,7 +3984,7 @@ Index: linux-2.4.21/drivers/usb/storage/datafab.c Index: linux-2.4.21/drivers/usb/storage/jumpshot.c =================================================================== --- linux-2.4.21.orig/drivers/usb/storage/jumpshot.c 2003-06-13 10:51:37.000000000 -0400 -+++ linux-2.4.21/drivers/usb/storage/jumpshot.c 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/drivers/usb/storage/jumpshot.c 2005-06-01 23:07:51.208561528 -0400 @@ -341,18 +341,30 @@ while (sg_idx < use_sg && transferred < len) { if (len - transferred >= sg[sg_idx].length - current_sg_offset) { @@ -4038,8 +4050,8 @@ Index: linux-2.4.21/drivers/usb/storage/jumpshot.c break; Index: linux-2.4.21/drivers/usb/storage/scsiglue.c =================================================================== ---- linux-2.4.21.orig/drivers/usb/storage/scsiglue.c 2004-12-21 13:51:15.000000000 -0500 -+++ linux-2.4.21/drivers/usb/storage/scsiglue.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/drivers/usb/storage/scsiglue.c 2005-06-01 22:51:52.000000000 -0400 ++++ linux-2.4.21/drivers/usb/storage/scsiglue.c 2005-06-01 23:07:51.209561376 -0400 @@ -606,7 +606,11 @@ } @@ -4114,8 +4126,8 @@ Index: linux-2.4.21/drivers/usb/storage/scsiglue.c element++; Index: linux-2.4.21/drivers/addon/ips_70015/ips.c =================================================================== ---- linux-2.4.21.orig/drivers/addon/ips_70015/ips.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/addon/ips_70015/ips.c 2004-12-21 14:02:57.000000000 -0500 +--- linux-2.4.21.orig/drivers/addon/ips_70015/ips.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/addon/ips_70015/ips.c 2005-06-01 23:07:51.214560616 -0400 @@ -207,7 +207,11 @@ #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) #include @@ -4130,8 +4142,8 @@ Index: linux-2.4.21/drivers/addon/ips_70015/ips.c #ifndef __devexit_p Index: linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c =================================================================== ---- linux-2.4.21.orig/drivers/addon/megaraid_2106/megaraid2.c 2004-12-21 13:51:19.000000000 -0500 -+++ linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c 2004-12-21 14:00:26.000000000 -0500 +--- linux-2.4.21.orig/drivers/addon/megaraid_2106/megaraid2.c 2005-06-01 22:51:54.000000000 -0400 ++++ linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c 2005-06-01 23:07:51.217560160 -0400 @@ -2198,7 +2198,11 @@ if( cmd->use_sg ) { sgl = (struct scatterlist *) @@ -4146,8 +4158,8 @@ Index: linux-2.4.21/drivers/addon/megaraid_2106/megaraid2.c c = *(u8 *)cmd->request_buffer; Index: linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c =================================================================== ---- linux-2.4.21.orig/drivers/addon/iscsi_sfnet/iscsi.c 2004-12-21 13:51:27.000000000 -0500 -+++ linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c 2004-12-21 14:37:53.000000000 -0500 +--- linux-2.4.21.orig/drivers/addon/iscsi_sfnet/iscsi.c 2005-06-01 22:51:59.000000000 -0400 ++++ linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c 2005-06-01 23:07:51.229558336 -0400 @@ -118,6 +118,16 @@ #include "iscsi-probe.h" #include "iscsi-crc.h" @@ -4282,8 +4294,8 @@ Index: linux-2.4.21/drivers/addon/iscsi_sfnet/iscsi.c } Index: linux-2.4.21/include/asm-i386/pci.h =================================================================== ---- linux-2.4.21.orig/include/asm-i386/pci.h 2004-12-21 13:51:05.000000000 -0500 -+++ linux-2.4.21/include/asm-i386/pci.h 2004-12-21 14:36:22.000000000 -0500 +--- linux-2.4.21.orig/include/asm-i386/pci.h 2005-06-01 22:51:45.000000000 -0400 ++++ linux-2.4.21/include/asm-i386/pci.h 2005-06-01 23:21:02.208311288 -0400 @@ -157,6 +157,17 @@ * temporary 2.4 hack */ @@ -4313,7 +4325,7 @@ Index: linux-2.4.21/include/asm-i386/pci.h Index: linux-2.4.21/include/asm-i386/scatterlist.h =================================================================== --- linux-2.4.21.orig/include/asm-i386/scatterlist.h 2002-11-28 18:53:15.000000000 -0500 -+++ linux-2.4.21/include/asm-i386/scatterlist.h 2004-12-21 13:58:10.000000000 -0500 ++++ linux-2.4.21/include/asm-i386/scatterlist.h 2005-06-01 23:07:51.230558184 -0400 @@ -19,7 +19,25 @@ * * and that's it. There's no excuse for not highmem enabling YOUR driver. /jens @@ -4350,8 +4362,8 @@ Index: linux-2.4.21/include/asm-i386/scatterlist.h #define ISA_DMA_THRESHOLD (0x00ffffff) Index: linux-2.4.21/net/xfrm/xfrm_algo.c =================================================================== ---- linux-2.4.21.orig/net/xfrm/xfrm_algo.c 2004-12-21 13:51:11.000000000 -0500 -+++ linux-2.4.21/net/xfrm/xfrm_algo.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/net/xfrm/xfrm_algo.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/net/xfrm/xfrm_algo.c 2005-06-01 23:07:51.231558032 -0400 @@ -487,9 +487,14 @@ if (copy > 0) { if (copy > len) @@ -4415,8 +4427,8 @@ Index: linux-2.4.21/net/xfrm/xfrm_algo.c if (!(len -= copy)) Index: linux-2.4.21/net/ipv6/addrconf.c =================================================================== ---- linux-2.4.21.orig/net/ipv6/addrconf.c 2004-12-21 13:51:11.000000000 -0500 -+++ linux-2.4.21/net/ipv6/addrconf.c 2004-12-21 13:58:10.000000000 -0500 +--- linux-2.4.21.orig/net/ipv6/addrconf.c 2005-06-01 22:51:50.000000000 -0400 ++++ linux-2.4.21/net/ipv6/addrconf.c 2005-06-01 23:07:51.232557880 -0400 @@ -1047,11 +1047,18 @@ u8 digest[16]; struct scatterlist sg[2]; diff --git a/lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch b/lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch new file mode 100644 index 0000000..a1063ae --- /dev/null +++ b/lustre/kernel_patches/patches/statfs64-cast-unsigned-2.4-rhel.patch @@ -0,0 +1,28 @@ +Index: linux-2.4.21/fs/open.c +=================================================================== +--- linux-2.4.21.orig/fs/open.c 2005-05-04 16:09:11.702098704 -0400 ++++ linux-2.4.21/fs/open.c 2005-05-04 16:17:31.597103112 -0400 +@@ -92,15 +92,15 @@ + if (retval) + return retval; + /* Stuff the 32 bit values into the 64 bit struct */ +- buf->f_type = st.f_type; +- buf->f_bsize = st.f_bsize; +- buf->f_blocks = st.f_blocks; +- buf->f_bfree = st.f_bfree; +- buf->f_bavail = st.f_bavail; +- buf->f_files = st.f_files; +- buf->f_ffree = st.f_ffree; ++ buf->f_type = (unsigned long) st.f_type; ++ buf->f_bsize = (unsigned long) st.f_bsize; ++ buf->f_blocks = (unsigned long) st.f_blocks; ++ buf->f_bfree = (unsigned long) st.f_bfree; ++ buf->f_bavail = (unsigned long) st.f_bavail; ++ buf->f_files = (unsigned long) st.f_files; ++ buf->f_ffree = (unsigned long) st.f_ffree; + buf->f_fsid = st.f_fsid; +- buf->f_namelen = st.f_namelen; ++ buf->f_namelen = (unsigned long) st.f_namelen; + memset(buf->f_spare, 0, sizeof(buf->f_spare)); + } + return 0; diff --git a/lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch b/lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch deleted file mode 100644 index 8ef6cd2..0000000 --- a/lustre/kernel_patches/patches/tcp-zero-copy-2.4.21-suse-171.patch +++ /dev/null @@ -1,456 +0,0 @@ -Index: linux-2.4.21-171/include/linux/skbuff.h -=================================================================== ---- linux-2.4.21-171.orig/include/linux/skbuff.h 2004-03-31 14:58:38.000000000 -0500 -+++ linux-2.4.21-171/include/linux/skbuff.h 2004-04-03 16:43:15.000000000 -0500 -@@ -116,6 +116,30 @@ - __u16 size; - }; - -+/* Support for callback when skb data has been released */ -+typedef struct zccd /* Zero Copy Callback Descriptor */ -+{ /* (embed as first member of custom struct) */ -+ atomic_t zccd_count; /* reference count */ -+ void (*zccd_destructor)(struct zccd *); /* callback when refcount reaches zero */ -+} zccd_t; -+ -+static inline void zccd_init (zccd_t *d, void (*callback)(zccd_t *)) -+{ -+ atomic_set (&d->zccd_count, 1); -+ d->zccd_destructor = callback; -+} -+ -+static inline void zccd_get (zccd_t *d) /* take a reference */ -+{ -+ atomic_inc (&d->zccd_count); -+} -+ -+static inline void zccd_put (zccd_t *d) /* release a reference */ -+{ -+ if (atomic_dec_and_test (&d->zccd_count)) -+ (d->zccd_destructor)(d); -+} -+ - /* This data is invariant across clones and lives at - * the end of the header data, ie. at skb->end. - */ -@@ -123,6 +147,12 @@ - atomic_t dataref; - unsigned int nr_frags; - struct sk_buff *frag_list; -+ zccd_t *zccd; /* zero copy descriptor */ -+ zccd_t *zccd2; /* 2nd zero copy descriptor */ -+ /* NB we expect zero-copy data to be at least 1 packet, so -+ * having 2 zccds means we don't unneccessarily split the packet -+ * where consecutive zero-copy sends abutt. -+ */ - skb_frag_t frags[MAX_SKB_FRAGS]; - }; - -Index: linux-2.4.21-171/include/net/tcp.h -=================================================================== ---- linux-2.4.21-171.orig/include/net/tcp.h 2004-03-31 15:07:31.000000000 -0500 -+++ linux-2.4.21-171/include/net/tcp.h 2004-04-03 16:46:15.000000000 -0500 -@@ -646,6 +646,8 @@ - - extern int tcp_sendmsg(struct sock *sk, struct msghdr *msg, int size); - extern ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags); -+extern ssize_t tcp_sendpage_zccd(struct socket *sock, struct page *page, int offset, ssize_t size, -+ int flags, zccd_t *zccd); - - extern int tcp_ioctl(struct sock *sk, - int cmd, -@@ -742,6 +744,9 @@ - struct msghdr *msg, - int len, int nonblock, - int flags, int *addr_len); -+extern int tcp_recvpackets(struct sock *sk, -+ struct sk_buff_head *packets, -+ int len, int nonblock); - - extern int tcp_listen_start(struct sock *sk); - -Index: linux-2.4.21-171/net/netsyms.c -=================================================================== ---- linux-2.4.21-171.orig/net/netsyms.c 2004-02-24 14:03:22.000000000 -0500 -+++ linux-2.4.21-171/net/netsyms.c 2004-04-03 16:13:53.000000000 -0500 -@@ -407,6 +407,8 @@ - - #endif - -+EXPORT_SYMBOL(tcp_sendpage_zccd); -+EXPORT_SYMBOL(tcp_recvpackets); - EXPORT_SYMBOL(tcp_read_sock); - - EXPORT_SYMBOL(netlink_set_err); -Index: linux-2.4.21-171/net/core/skbuff.c -=================================================================== ---- linux-2.4.21-171.orig/net/core/skbuff.c 2004-02-24 14:03:22.000000000 -0500 -+++ linux-2.4.21-171/net/core/skbuff.c 2004-04-03 16:13:53.000000000 -0500 -@@ -208,6 +208,8 @@ - atomic_set(&(skb_shinfo(skb)->dataref), 1); - skb_shinfo(skb)->nr_frags = 0; - skb_shinfo(skb)->frag_list = NULL; -+ skb_shinfo(skb)->zccd = NULL; /* skbuffs kick off with NO user zero copy descriptors */ -+ skb_shinfo(skb)->zccd2 = NULL; - return skb; - - nodata: -@@ -277,6 +279,10 @@ - { - if (!skb->cloned || - atomic_dec_and_test(&(skb_shinfo(skb)->dataref))) { -+ if (skb_shinfo(skb)->zccd != NULL) /* zero copy callback descriptor? */ -+ zccd_put (skb_shinfo(skb)->zccd); /* release hold */ -+ if (skb_shinfo(skb)->zccd2 != NULL) /* 2nd zero copy callback descriptor? */ -+ zccd_put (skb_shinfo(skb)->zccd2); /* release hold */ - if (skb_shinfo(skb)->nr_frags) { - int i; - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) -@@ -535,6 +541,8 @@ - atomic_set(&(skb_shinfo(skb)->dataref), 1); - skb_shinfo(skb)->nr_frags = 0; - skb_shinfo(skb)->frag_list = NULL; -+ skb_shinfo(skb)->zccd = NULL; /* copied data => no user zero copy descriptor */ -+ skb_shinfo(skb)->zccd2 = NULL; - - /* We are no longer a clone, even if we were. */ - skb->cloned = 0; -@@ -581,6 +589,14 @@ - n->data_len = skb->data_len; - n->len = skb->len; - -+ if (skb_shinfo(skb)->zccd != NULL) /* user zero copy descriptor? */ -+ zccd_get (skb_shinfo(skb)->zccd); /* 1 more ref (pages are shared) */ -+ skb_shinfo(n)->zccd = skb_shinfo(skb)->zccd; -+ -+ if (skb_shinfo(skb)->zccd2 != NULL) /* 2nd user zero copy descriptor? */ -+ zccd_get (skb_shinfo(skb)->zccd2); /* 1 more ref (pages are shared) */ -+ skb_shinfo(n)->zccd2 = skb_shinfo(skb)->zccd2; -+ - if (skb_shinfo(skb)->nr_frags) { - int i; - -@@ -623,6 +639,8 @@ - u8 *data; - int size = nhead + (skb->end - skb->head) + ntail; - long off; -+ zccd_t *zccd = skb_shinfo(skb)->zccd; /* stash user zero copy descriptor */ -+ zccd_t *zccd2 = skb_shinfo(skb)->zccd2; /* stash 2nd user zero copy descriptor */ - - if (skb_shared(skb)) - BUG(); -@@ -644,6 +662,11 @@ - if (skb_shinfo(skb)->frag_list) - skb_clone_fraglist(skb); - -+ if (zccd != NULL) /* user zero copy descriptor? */ -+ zccd_get (zccd); /* extra ref (pages are shared) */ -+ if (zccd2 != NULL) /* 2nd user zero copy descriptor? */ -+ zccd_get (zccd2); /* extra ref (pages are shared) */ -+ - skb_release_data(skb); - - off = (data+nhead) - skb->head; -@@ -658,6 +681,8 @@ - skb->nh.raw += off; - skb->cloned = 0; - atomic_set(&skb_shinfo(skb)->dataref, 1); -+ skb_shinfo(skb)->zccd = zccd; -+ skb_shinfo(skb)->zccd2 = zccd2; - return 0; - - nodata: -Index: linux-2.4.21-171/net/ipv4/tcp.c -=================================================================== ---- linux-2.4.21-171.orig/net/ipv4/tcp.c 2004-02-24 13:42:30.000000000 -0500 -+++ linux-2.4.21-171/net/ipv4/tcp.c 2004-04-03 16:43:05.000000000 -0500 -@@ -748,7 +748,7 @@ - goto out; - } - --ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags); -+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags, zccd_t *zccd); - - static inline int - can_coalesce(struct sk_buff *skb, int i, struct page *page, int off) -@@ -827,7 +827,7 @@ - return err; - } - --ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags) -+ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset, size_t psize, int flags, zccd_t *zccd) - { - struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); - int mss_now; -@@ -875,6 +875,17 @@ - copy = size; - - i = skb_shinfo(skb)->nr_frags; -+ -+ if (zccd != NULL && /* this is a zcc I/O */ -+ skb_shinfo(skb)->zccd != NULL && /* skb is part of a zcc I/O */ -+ skb_shinfo(skb)->zccd2 != NULL && -+ skb_shinfo(skb)->zccd != zccd && /* not the same one */ -+ skb_shinfo(skb)->zccd2 != zccd) -+ { -+ tcp_mark_push (tp, skb); -+ goto new_segment; -+ } -+ - if (can_coalesce(skb, i, page, offset)) { - skb_shinfo(skb)->frags[i-1].size += copy; - } else if (i < MAX_SKB_FRAGS) { -@@ -885,6 +896,20 @@ - goto new_segment; - } - -+ if (zccd != NULL && /* this is a zcc I/O */ -+ skb_shinfo(skb)->zccd != zccd && /* not already referencing this zccd */ -+ skb_shinfo(skb)->zccd2 != zccd) -+ { -+ zccd_get (zccd); /* bump ref count */ -+ -+ BUG_TRAP (skb_shinfo(skb)->zccd2 == NULL); -+ -+ if (skb_shinfo(skb)->zccd == NULL) /* reference this zccd */ -+ skb_shinfo(skb)->zccd = zccd; -+ else -+ skb_shinfo(skb)->zccd2 = zccd; -+ } -+ - skb->len += copy; - skb->data_len += copy; - skb->ip_summed = CHECKSUM_HW; -@@ -948,7 +973,28 @@ - - lock_sock(sk); - TCP_CHECK_TIMER(sk); -- res = do_tcp_sendpages(sk, &page, offset, size, flags); -+ res = do_tcp_sendpages(sk, &page, offset, size, flags, NULL); -+ TCP_CHECK_TIMER(sk); -+ release_sock(sk); -+ return res; -+} -+ -+ssize_t tcp_sendpage_zccd(struct socket *sock, struct page *page, int offset, ssize_t size, -+ int flags, zccd_t *zccd) -+{ -+ ssize_t res; -+ struct sock *sk = sock->sk; -+ -+#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) -+ -+ if (!(sk->route_caps & NETIF_F_SG) || /* caller shouldn't waste her time */ -+ !(sk->route_caps & TCP_ZC_CSUM_FLAGS)) /* on double mapping */ -+ BUG (); -+ -+ lock_sock(sk); -+ TCP_CHECK_TIMER(sk); -+ -+ res = do_tcp_sendpages(sk, &page, 0, size, flags, zccd); - TCP_CHECK_TIMER(sk); - release_sock(sk); - return res; -@@ -1772,6 +1818,202 @@ - goto out; - } - -+int tcp_recvpackets (struct sock *sk, struct sk_buff_head *packets, -+ int len, int nonblock) -+{ -+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp); -+ int copied; -+ long timeo; -+ -+ BUG_TRAP (len > 0); -+ /*BUG_TRAP ((flags & (MSG_OOB | MSG_PEEK | MSG_TRUNC)) == 0);*/ -+ -+ lock_sock(sk); -+ -+ TCP_CHECK_TIMER(sk); -+ -+ copied = -ENOTCONN; -+ if (sk->state == TCP_LISTEN) -+ goto out; -+ -+ copied = 0; -+ timeo = sock_rcvtimeo(sk, nonblock); -+ -+ do { -+ struct sk_buff * skb; -+ u32 offset; -+ unsigned long used; -+ int exhausted; -+ int eaten; -+ -+ /* Are we at urgent data? Stop if we have read anything. */ -+ if (copied && tp->urg_data && tp->urg_seq == tp->copied_seq) -+ break; -+ -+ /* We need to check signals first, to get correct SIGURG -+ * handling. FIXME: Need to check this doesnt impact 1003.1g -+ * and move it down to the bottom of the loop -+ */ -+ if (signal_pending(current)) { -+ if (copied) -+ break; -+ copied = timeo ? sock_intr_errno(timeo) : -EAGAIN; -+ break; -+ } -+ -+ /* Next get a buffer. */ -+ -+ skb = skb_peek(&sk->receive_queue); -+ -+ if (skb == NULL) /* nothing ready */ -+ { -+ if (copied) { -+ if (sk->err || -+ sk->state == TCP_CLOSE || -+ (sk->shutdown & RCV_SHUTDOWN) || -+ !timeo || -+ (0)) -+ break; -+ } else { -+ if (sk->done) -+ break; -+ -+ if (sk->err) { -+ copied = sock_error(sk); -+ break; -+ } -+ -+ if (sk->shutdown & RCV_SHUTDOWN) -+ break; -+ -+ if (sk->state == TCP_CLOSE) { -+ if (!sk->done) { -+ /* This occurs when user tries to read -+ * from never connected socket. -+ */ -+ copied = -ENOTCONN; -+ break; -+ } -+ break; -+ } -+ -+ if (!timeo) { -+ copied = -EAGAIN; -+ break; -+ } -+ } -+ -+ cleanup_rbuf(sk, copied); -+ timeo = tcp_data_wait(sk, timeo); -+ continue; -+ } -+ -+ BUG_TRAP (atomic_read (&skb->users) == 1); -+ -+ exhausted = eaten = 0; -+ -+ offset = tp->copied_seq - TCP_SKB_CB(skb)->seq; -+ if (skb->h.th->syn) -+ offset--; -+ -+ used = skb->len - offset; -+ -+ if (tp->urg_data) { -+ u32 urg_offset = tp->urg_seq - tp->copied_seq; -+ if (urg_offset < used) { -+ if (!urg_offset) { /* at urgent date */ -+ if (!sk->urginline) { -+ tp->copied_seq++; /* discard the single byte of urgent data */ -+ offset++; -+ used--; -+ } -+ } else /* truncate read */ -+ used = urg_offset; -+ } -+ } -+ -+ BUG_TRAP (used >= 0); -+ if (len < used) -+ used = len; -+ -+ if (used == 0) -+ exhausted = 1; -+ else -+ { -+ if (skb_is_nonlinear (skb)) -+ { -+ int rc = skb_linearize (skb, GFP_KERNEL); -+ -+ printk ("tcp_recvpackets(): linearising: %d\n", rc); -+ -+ if (rc) -+ { -+ if (!copied) -+ copied = rc; -+ break; -+ } -+ } -+ -+ if ((offset + used) == skb->len) /* consuming the whole packet */ -+ { -+ __skb_unlink (skb, &sk->receive_queue); -+ dst_release (skb->dst); -+ skb_orphan (skb); -+ __skb_pull (skb, offset); -+ __skb_queue_tail (packets, skb); -+ exhausted = eaten = 1; -+ } -+ else /* consuming only part of the packet */ -+ { -+ struct sk_buff *skb2 = skb_clone (skb, GFP_KERNEL); -+ -+ if (skb2 == NULL) -+ { -+ if (!copied) -+ copied = -ENOMEM; -+ break; -+ } -+ -+ dst_release (skb2->dst); -+ __skb_pull (skb2, offset); -+ __skb_trim (skb2, used); -+ __skb_queue_tail (packets, skb2); -+ } -+ -+ tp->copied_seq += used; -+ copied += used; -+ len -= used; -+ } -+ -+ if (tp->urg_data && after(tp->copied_seq,tp->urg_seq)) { -+ tp->urg_data = 0; -+ tcp_fast_path_check(sk, tp); -+ } -+ -+ if (!exhausted) -+ continue; -+ -+ if (skb->h.th->fin) -+ { -+ tp->copied_seq++; -+ if (!eaten) -+ tcp_eat_skb (sk, skb); -+ break; -+ } -+ -+ if (!eaten) -+ tcp_eat_skb (sk, skb); -+ -+ } while (len > 0); -+ -+ out: -+ /* Clean up data we have read: This will do ACK frames. */ -+ cleanup_rbuf(sk, copied); -+ TCP_CHECK_TIMER(sk); -+ release_sock(sk); -+ return copied; -+} -+ - /* - * State processing on a close. This implements the state shift for - * sending our FIN frame. Note that we only send a FIN for some diff --git a/lustre/kernel_patches/patches/uml-exprt-clearuser.patch b/lustre/kernel_patches/patches/uml-exprt-clearuser.patch new file mode 100644 index 0000000..22992c1 --- /dev/null +++ b/lustre/kernel_patches/patches/uml-exprt-clearuser.patch @@ -0,0 +1,24 @@ +Index: uml-2.4.24/arch/um/kernel/tt/ksyms.c +=================================================================== +--- uml-2.4.24.orig/arch/um/kernel/tt/ksyms.c 2005-05-04 13:59:58.000000000 +0300 ++++ uml-2.4.24/arch/um/kernel/tt/ksyms.c 2005-05-05 23:49:51.508354472 +0300 +@@ -12,6 +12,7 @@ + EXPORT_SYMBOL(__do_strncpy_from_user); + EXPORT_SYMBOL(__do_strnlen_user); + EXPORT_SYMBOL(__do_clear_user); ++EXPORT_SYMBOL(clear_user_tt); + + EXPORT_SYMBOL(tracing_pid); + EXPORT_SYMBOL(honeypot); +Index: uml-2.4.24/arch/um/kernel/ksyms.c +=================================================================== +--- uml-2.4.24.orig/arch/um/kernel/ksyms.c 2005-05-04 13:54:24.000000000 +0300 ++++ uml-2.4.24/arch/um/kernel/ksyms.c 2005-05-05 23:50:19.649076432 +0300 +@@ -53,6 +53,7 @@ + #ifdef CONFIG_MODE_SKAS + EXPORT_SYMBOL(copy_to_user_skas); + EXPORT_SYMBOL(copy_from_user_skas); ++EXPORT_SYMBOL(clear_user_skas); + #endif + + EXPORT_SYMBOL(os_stat_fd); diff --git a/lustre/kernel_patches/patches/uml-patch-2.4.24-1.patch b/lustre/kernel_patches/patches/uml-patch-2.4.24-1.patch index ade809d..9b62f6e 100644 --- a/lustre/kernel_patches/patches/uml-patch-2.4.24-1.patch +++ b/lustre/kernel_patches/patches/uml-patch-2.4.24-1.patch @@ -13455,7 +13455,7 @@ diff -Naur -X ../exclude-files orig/arch/um/fs/hostfs/hostfs_user.c um/arch/um/f + if(chown(file, -1, attrs->ia_gid)) return(-errno); + } + if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ -+ if(truncate(file, attrs->ia_size)) return(-errno); ++ if(truncate64(file, attrs->ia_size)) return(-errno); + } + ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; + if((attrs->ia_valid & ma) == ma){ diff --git a/lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch b/lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch new file mode 100644 index 0000000..16f449d --- /dev/null +++ b/lustre/kernel_patches/patches/uml-patch-2.4.29-1.patch @@ -0,0 +1,46719 @@ +Index: linux-2.4.29/arch/um/config_block.in +=================================================================== +--- linux-2.4.29.orig/arch/um/config_block.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config_block.in 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,22 @@ ++mainmenu_option next_comment ++comment 'Block Devices' ++ ++bool 'Virtual block device' CONFIG_BLK_DEV_UBD ++dep_bool ' Always do synchronous disk IO for UBD' CONFIG_BLK_DEV_UBD_SYNC $CONFIG_BLK_DEV_UBD ++bool 'COW device' CONFIG_COW ++ ++if [ "$CONFIG_BLK_DEV_UBD" = "y" -o "$CONFIG_COW" = "y" ] ; then ++ define_bool CONFIG_COW_COMMON y ++fi ++ ++tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP ++dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET ++tristate 'RAM disk support' CONFIG_BLK_DEV_RAM ++if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then ++ int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 ++fi ++dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM ++ ++tristate 'Example IO memory driver' CONFIG_MMAPPER ++ ++endmenu +Index: linux-2.4.29/arch/um/config_char.in +=================================================================== +--- linux-2.4.29.orig/arch/um/config_char.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config_char.in 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,37 @@ ++mainmenu_option next_comment ++comment 'Character Devices' ++ ++define_bool CONFIG_STDIO_CONSOLE y ++ ++bool 'Virtual serial line' CONFIG_SSL ++ ++bool 'file descriptor channel support' CONFIG_FD_CHAN ++bool 'null channel support' CONFIG_NULL_CHAN ++bool 'port channel support' CONFIG_PORT_CHAN ++bool 'pty channel support' CONFIG_PTY_CHAN ++bool 'tty channel support' CONFIG_TTY_CHAN ++bool 'xterm channel support' CONFIG_XTERM_CHAN ++string 'Default main console channel initialization' CONFIG_CON_ZERO_CHAN \ ++ "fd:0,fd:1" ++string 'Default console channel initialization' CONFIG_CON_CHAN "xterm" ++string 'Default serial line channel initialization' CONFIG_SSL_CHAN "pty" ++ ++ ++bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS ++if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then ++ int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 ++fi ++ ++bool 'Watchdog Timer Support' CONFIG_WATCHDOG ++dep_bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT \ ++ $CONFIG_WATCHDOG ++dep_tristate ' Software Watchdog' CONFIG_SOFT_WATCHDOG $CONFIG_WATCHDOG ++dep_tristate ' UML watchdog' CONFIG_UML_WATCHDOG $CONFIG_WATCHDOG ++ ++tristate 'Sound support' CONFIG_UML_SOUND ++define_tristate CONFIG_SOUND $CONFIG_UML_SOUND ++define_tristate CONFIG_HOSTAUDIO $CONFIG_UML_SOUND ++ ++bool 'Enable tty logging' CONFIG_TTY_LOG ++ ++endmenu +Index: linux-2.4.29/arch/um/config.in +=================================================================== +--- linux-2.4.29.orig/arch/um/config.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config.in 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,123 @@ ++define_bool CONFIG_USERMODE y ++ ++mainmenu_name "Linux/Usermode Kernel Configuration" ++ ++define_bool CONFIG_ISA n ++define_bool CONFIG_SBUS n ++define_bool CONFIG_PCI n ++ ++define_bool CONFIG_UID16 y ++ ++define_bool CONFIG_RWSEM_XCHGADD_ALGORITHM y ++ ++mainmenu_option next_comment ++comment 'Code maturity level options' ++bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL ++endmenu ++ ++mainmenu_option next_comment ++comment 'General Setup' ++ ++bool 'Separate kernel address space support' CONFIG_MODE_SKAS ++ ++# This is to ensure that at least one of the modes is enabled. When neither ++# is present in defconfig, they default to N, which is bad. ++if [ "$CONFIG_MODE_SKAS" != "y" ]; then ++ define_bool CONFIG_MODE_TT y ++fi ++ ++bool 'Tracing thread support' CONFIG_MODE_TT ++if [ "$CONFIG_MODE_TT" != "y" ]; then ++ bool 'Statically linked binary when CONFIG_MODE_TT is disabled' CONFIG_STATIC_LINK ++fi ++bool 'Networking support' CONFIG_NET ++bool 'System V IPC' CONFIG_SYSVIPC ++bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT ++bool 'Sysctl support' CONFIG_SYSCTL ++tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT ++tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF ++tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC ++ ++tristate 'Host filesystem' CONFIG_HOSTFS ++tristate 'Usable host filesystem' CONFIG_HUMFS ++ ++if [ "$CONFIG_HOSTFS" = "y" -o "$CONFIG_HUMFS" = "y" ]; then ++ define_tristate CONFIG_EXTERNFS y ++fi ++ ++tristate 'Honeypot proc filesystem' CONFIG_HPPFS ++bool 'Management console' CONFIG_MCONSOLE ++dep_bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ $CONFIG_MCONSOLE ++bool '2G/2G host address space split' CONFIG_HOST_2G_2G ++ ++bool 'Symmetric multi-processing support' CONFIG_UML_SMP ++define_bool CONFIG_SMP $CONFIG_UML_SMP ++if [ "$CONFIG_SMP" = "y" ]; then ++ int 'Maximum number of CPUs (2-32)' CONFIG_NR_CPUS 32 ++fi ++ ++int 'Nesting level' CONFIG_NEST_LEVEL 0 ++int 'Kernel address space size (in .5G units)' CONFIG_KERNEL_HALF_GIGS 1 ++bool 'Highmem support' CONFIG_HIGHMEM ++bool '/proc/mm' CONFIG_PROC_MM ++int 'Kernel stack size order' CONFIG_KERNEL_STACK_ORDER 2 ++bool 'Real-time Clock' CONFIG_UML_REAL_TIME_CLOCK ++endmenu ++ ++mainmenu_option next_comment ++comment 'Loadable module support' ++bool 'Enable loadable module support' CONFIG_MODULES ++if [ "$CONFIG_MODULES" = "y" ]; then ++# MODVERSIONS does not yet work in this architecture ++# bool ' Set version information on all module symbols' CONFIG_MODVERSIONS ++ bool ' Kernel module loader' CONFIG_KMOD ++fi ++endmenu ++ ++source arch/um/config_char.in ++ ++source arch/um/config_block.in ++ ++define_bool CONFIG_NETDEVICES $CONFIG_NET ++ ++if [ "$CONFIG_NET" = "y" ]; then ++ source arch/um/config_net.in ++ source net/Config.in ++fi ++ ++source fs/Config.in ++ ++mainmenu_option next_comment ++comment 'SCSI support' ++ ++tristate 'SCSI support' CONFIG_SCSI ++ ++if [ "$CONFIG_SCSI" != "n" ]; then ++ source arch/um/config_scsi.in ++fi ++endmenu ++ ++source drivers/md/Config.in ++ ++source drivers/mtd/Config.in ++ ++source lib/Config.in ++ ++source crypto/Config.in ++ ++mainmenu_option next_comment ++comment 'Kernel hacking' ++bool 'Debug memory allocations' CONFIG_DEBUG_SLAB ++bool 'Enable kernel debugging symbols' CONFIG_DEBUGSYM ++if [ "$CONFIG_XTERM_CHAN" = "y" ]; then ++ dep_bool 'Enable ptrace proxy' CONFIG_PT_PROXY $CONFIG_DEBUGSYM ++else ++ define_bool CONFIG_PT_PROXY n ++fi ++ ++if [ "$CONFIG_MODE_TT" = "n" ]; then ++ dep_bool 'Enable gprof support' CONFIG_GPROF $CONFIG_DEBUGSYM ++fi ++ ++dep_bool 'Enable gcov support' CONFIG_GCOV $CONFIG_DEBUGSYM ++endmenu +Index: linux-2.4.29/arch/um/config_net.in +=================================================================== +--- linux-2.4.29.orig/arch/um/config_net.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config_net.in 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,48 @@ ++mainmenu_option next_comment ++comment 'Network Devices' ++ ++# UML virtual driver ++bool 'Virtual network device' CONFIG_UML_NET ++ ++dep_bool ' Ethertap transport' CONFIG_UML_NET_ETHERTAP $CONFIG_UML_NET ++dep_bool ' TUN/TAP transport' CONFIG_UML_NET_TUNTAP $CONFIG_UML_NET ++dep_bool ' SLIP transport' CONFIG_UML_NET_SLIP $CONFIG_UML_NET ++dep_bool ' SLiRP transport' CONFIG_UML_NET_SLIRP $CONFIG_UML_NET ++dep_bool ' Daemon transport' CONFIG_UML_NET_DAEMON $CONFIG_UML_NET ++dep_bool ' Multicast transport' CONFIG_UML_NET_MCAST $CONFIG_UML_NET ++dep_bool ' pcap transport' CONFIG_UML_NET_PCAP $CONFIG_UML_NET ++ ++# Below are hardware-independent drivers mirrored from ++# drivers/net/Config.in. It would be nice if Linux ++# had HW independent drivers separated from the other ++# but it does not. Until then each non-ISA/PCI arch ++# needs to provide it's own menu of network drivers ++ ++tristate 'Dummy net driver support' CONFIG_DUMMY ++tristate 'Bonding driver support' CONFIG_BONDING ++tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER ++tristate 'Universal TUN/TAP device driver support' CONFIG_TUN ++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then ++ if [ "$CONFIG_NETLINK" = "y" ]; then ++ tristate 'Ethertap network tap (OBSOLETE)' CONFIG_ETHERTAP ++ fi ++fi ++ ++tristate 'PPP (point-to-point protocol) support' CONFIG_PPP ++if [ ! "$CONFIG_PPP" = "n" ]; then ++ dep_bool ' PPP multilink support (EXPERIMENTAL)' CONFIG_PPP_MULTILINK $CONFIG_EXPERIMENTAL ++ dep_bool ' PPP filtering' CONFIG_PPP_FILTER $CONFIG_FILTER ++ dep_tristate ' PPP support for async serial ports' CONFIG_PPP_ASYNC $CONFIG_PPP ++ dep_tristate ' PPP support for sync tty ports' CONFIG_PPP_SYNC_TTY $CONFIG_PPP ++ dep_tristate ' PPP Deflate compression' CONFIG_PPP_DEFLATE $CONFIG_PPP ++ dep_tristate ' PPP BSD-Compress compression' CONFIG_PPP_BSDCOMP $CONFIG_PPP ++ dep_tristate ' PPP over Ethernet (EXPERIMENTAL)' CONFIG_PPPOE $CONFIG_PPP $CONFIG_EXPERIMENTAL ++ dep_tristate ' PPP MPPE compression (encryption)' CONFIG_PPP_MPPE $CONFIG_PPP ++fi ++ ++tristate 'SLIP (serial line) support' CONFIG_SLIP ++dep_bool ' CSLIP compressed headers' CONFIG_SLIP_COMPRESSED $CONFIG_SLIP ++dep_bool ' Keepalive and linefill' CONFIG_SLIP_SMART $CONFIG_SLIP ++dep_bool ' Six bit SLIP encapsulation' CONFIG_SLIP_MODE_SLIP6 $CONFIG_SLIP ++ ++endmenu +Index: linux-2.4.29/arch/um/config.release +=================================================================== +--- linux-2.4.29.orig/arch/um/config.release 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config.release 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,302 @@ ++# ++# Automatically generated make config: don't edit ++# ++CONFIG_USERMODE=y ++# CONFIG_ISA is not set ++# CONFIG_SBUS is not set ++# CONFIG_PCI is not set ++CONFIG_UID16=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++ ++# ++# General Setup ++# ++CONFIG_NET=y ++CONFIG_SYSVIPC=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_SYSCTL=y ++CONFIG_BINFMT_AOUT=y ++CONFIG_BINFMT_ELF=y ++CONFIG_BINFMT_MISC=y ++CONFIG_HOSTFS=y ++# CONFIG_HPPFS is not set ++CONFIG_MCONSOLE=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_HOST_2G_2G is not set ++# CONFIG_UML_SMP is not set ++# CONFIG_SMP is not set ++CONFIG_NEST_LEVEL=0 ++CONFIG_KERNEL_HALF_GIGS=1 ++ ++# ++# Loadable module support ++# ++CONFIG_MODULES=y ++CONFIG_KMOD=y ++ ++# ++# Character Devices ++# ++CONFIG_STDIO_CONSOLE=y ++CONFIG_SSL=y ++CONFIG_FD_CHAN=y ++# CONFIG_NULL_CHAN is not set ++CONFIG_PORT_CHAN=y ++CONFIG_PTY_CHAN=y ++CONFIG_TTY_CHAN=y ++CONFIG_XTERM_CHAN=y ++CONFIG_CON_ZERO_CHAN="fd:0,fd:1" ++CONFIG_CON_CHAN="xterm" ++CONFIG_SSL_CHAN="pty" ++CONFIG_UNIX98_PTYS=y ++CONFIG_UNIX98_PTY_COUNT=256 ++# CONFIG_WATCHDOG is not set ++CONFIG_UML_SOUND=y ++CONFIG_SOUND=y ++CONFIG_HOSTAUDIO=y ++# CONFIG_TTY_LOG is not set ++ ++# ++# Block Devices ++# ++CONFIG_BLK_DEV_UBD=y ++# CONFIG_BLK_DEV_UBD_SYNC is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_INITRD=y ++# CONFIG_MMAPPER is not set ++CONFIG_NETDEVICES=y ++ ++# ++# Network Devices ++# ++CONFIG_UML_NET=y ++CONFIG_UML_NET_ETHERTAP=y ++CONFIG_UML_NET_TUNTAP=y ++CONFIG_UML_NET_SLIP=y ++CONFIG_UML_NET_DAEMON=y ++CONFIG_UML_NET_MCAST=y ++CONFIG_DUMMY=y ++CONFIG_BONDING=m ++CONFIG_EQUALIZER=m ++CONFIG_TUN=y ++CONFIG_PPP=m ++CONFIG_PPP_MULTILINK=y ++# CONFIG_PPP_ASYNC is not set ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPPOE=m ++CONFIG_SLIP=m ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_PACKET_MMAP=y ++# CONFIG_NETLINK_DEV is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_FILTER is not set ++CONFIG_UNIX=y ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++# CONFIG_IP_PNP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_INET_ECN is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_IPV6 is not set ++# CONFIG_KHTTPD is not set ++# CONFIG_ATM is not set ++# CONFIG_VLAN_8021Q is not set ++ ++# ++# ++# ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++ ++# ++# Appletalk devices ++# ++# CONFIG_DECNET is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_LLC is not set ++# CONFIG_NET_DIVERT is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_FASTROUTE is not set ++# CONFIG_NET_HW_FLOWCONTROL is not set ++ ++# ++# QoS and/or fair queueing ++# ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++ ++# ++# File systems ++# ++CONFIG_QUOTA=y ++CONFIG_AUTOFS_FS=m ++CONFIG_AUTOFS4_FS=m ++CONFIG_REISERFS_FS=m ++# CONFIG_REISERFS_CHECK is not set ++# CONFIG_REISERFS_PROC_INFO is not set ++CONFIG_ADFS_FS=m ++# CONFIG_ADFS_FS_RW is not set ++CONFIG_AFFS_FS=m ++CONFIG_HFS_FS=m ++CONFIG_BFS_FS=m ++CONFIG_EXT3_FS=y ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_UMSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_EFS_FS=m ++CONFIG_CRAMFS=m ++CONFIG_TMPFS=y ++CONFIG_RAMFS=y ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++CONFIG_MINIX_FS=m ++CONFIG_VXFS_FS=m ++# CONFIG_NTFS_FS is not set ++CONFIG_HPFS_FS=m ++CONFIG_PROC_FS=y ++CONFIG_DEVFS_FS=y ++CONFIG_DEVFS_MOUNT=y ++# CONFIG_DEVFS_DEBUG is not set ++CONFIG_DEVPTS_FS=y ++CONFIG_QNX4FS_FS=m ++# CONFIG_QNX4FS_RW is not set ++CONFIG_ROMFS_FS=m ++CONFIG_EXT2_FS=y ++CONFIG_SYSV_FS=m ++CONFIG_UDF_FS=m ++# CONFIG_UDF_RW is not set ++CONFIG_UFS_FS=m ++# CONFIG_UFS_FS_WRITE is not set ++ ++# ++# Network File Systems ++# ++# CONFIG_CODA_FS is not set ++# CONFIG_INTERMEZZO_FS is not set ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFSD=y ++CONFIG_NFSD_V3=y ++CONFIG_SUNRPC=y ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++# CONFIG_SMB_FS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_ZISOFS_FS is not set ++CONFIG_ZLIB_FS_INFLATE=m ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_SMB_NLS is not set ++CONFIG_NLS=y ++ ++# ++# Native Language Support ++# ++CONFIG_NLS_DEFAULT="iso8859-1" ++# CONFIG_NLS_CODEPAGE_437 is not set ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ISO8859_1 is not set ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# SCSI support ++# ++CONFIG_SCSI=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++# CONFIG_BLK_DEV_SD is not set ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_DEBUG_QUEUES is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++CONFIG_SCSI_DEBUG=m ++ ++# ++# Multi-device support (RAID and LVM) ++# ++# CONFIG_MD is not set ++ ++# ++# Memory Technology Devices (MTD) ++# ++# CONFIG_MTD is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_DEBUG_SLAB is not set ++# CONFIG_DEBUGSYM is not set +Index: linux-2.4.29/arch/um/config_scsi.in +=================================================================== +--- linux-2.4.29.orig/arch/um/config_scsi.in 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/config_scsi.in 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,30 @@ ++comment 'SCSI support type (disk, tape, CD-ROM)' ++ ++dep_tristate ' SCSI disk support' CONFIG_BLK_DEV_SD $CONFIG_SCSI ++ ++if [ "$CONFIG_BLK_DEV_SD" != "n" ]; then ++ int 'Maximum number of SCSI disks that can be loaded as modules' CONFIG_SD_EXTRA_DEVS 40 ++fi ++ ++dep_tristate ' SCSI tape support' CONFIG_CHR_DEV_ST $CONFIG_SCSI ++ ++dep_tristate ' SCSI CD-ROM support' CONFIG_BLK_DEV_SR $CONFIG_SCSI ++ ++if [ "$CONFIG_BLK_DEV_SR" != "n" ]; then ++ bool ' Enable vendor-specific extensions (for SCSI CDROM)' CONFIG_BLK_DEV_SR_VENDOR ++ int 'Maximum number of CDROM devices that can be loaded as modules' CONFIG_SR_EXTRA_DEVS 2 ++fi ++dep_tristate ' SCSI generic support' CONFIG_CHR_DEV_SG $CONFIG_SCSI ++ ++comment 'Some SCSI devices (e.g. CD jukebox) support multiple LUNs' ++ ++#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then ++ bool ' Enable extra checks in new queueing code' CONFIG_SCSI_DEBUG_QUEUES ++#fi ++ ++bool ' Probe all LUNs on each SCSI device' CONFIG_SCSI_MULTI_LUN ++ ++bool ' Verbose SCSI error reporting (kernel size +=12K)' CONFIG_SCSI_CONSTANTS ++bool ' SCSI logging facility' CONFIG_SCSI_LOGGING ++ ++dep_tristate 'SCSI debugging host simulator (EXPERIMENTAL)' CONFIG_SCSI_DEBUG $CONFIG_SCSI +Index: linux-2.4.29/arch/um/defconfig +=================================================================== +--- linux-2.4.29.orig/arch/um/defconfig 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/defconfig 2005-05-03 22:28:14.000000000 +0300 +@@ -0,0 +1,430 @@ ++# ++# Automatically generated make config: don't edit ++# ++CONFIG_USERMODE=y ++# CONFIG_ISA is not set ++# CONFIG_SBUS is not set ++# CONFIG_PCI is not set ++CONFIG_UID16=y ++CONFIG_RWSEM_XCHGADD_ALGORITHM=y ++ ++# ++# Code maturity level options ++# ++CONFIG_EXPERIMENTAL=y ++ ++# ++# General Setup ++# ++CONFIG_MODE_SKAS=y ++CONFIG_MODE_TT=y ++CONFIG_NET=y ++CONFIG_SYSVIPC=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_SYSCTL=y ++CONFIG_BINFMT_AOUT=y ++CONFIG_BINFMT_ELF=y ++CONFIG_BINFMT_MISC=y ++CONFIG_HOSTFS=y ++CONFIG_HUMFS=y ++CONFIG_EXTERNFS=y ++CONFIG_HPPFS=y ++CONFIG_MCONSOLE=y ++CONFIG_MAGIC_SYSRQ=y ++# CONFIG_HOST_2G_2G is not set ++# CONFIG_UML_SMP is not set ++# CONFIG_SMP is not set ++CONFIG_NEST_LEVEL=0 ++CONFIG_KERNEL_HALF_GIGS=1 ++# CONFIG_HIGHMEM is not set ++CONFIG_PROC_MM=y ++CONFIG_KERNEL_STACK_ORDER=2 ++CONFIG_UML_REAL_TIME_CLOCK=y ++ ++# ++# Loadable module support ++# ++CONFIG_MODULES=y ++# CONFIG_KMOD is not set ++ ++# ++# Character Devices ++# ++CONFIG_STDIO_CONSOLE=y ++CONFIG_SSL=y ++CONFIG_FD_CHAN=y ++CONFIG_NULL_CHAN=y ++CONFIG_PORT_CHAN=y ++CONFIG_PTY_CHAN=y ++CONFIG_TTY_CHAN=y ++CONFIG_XTERM_CHAN=y ++CONFIG_CON_ZERO_CHAN="fd:0,fd:1" ++CONFIG_CON_CHAN="xterm" ++CONFIG_SSL_CHAN="pty" ++CONFIG_UNIX98_PTYS=y ++CONFIG_UNIX98_PTY_COUNT=256 ++# CONFIG_WATCHDOG is not set ++# CONFIG_WATCHDOG_NOWAYOUT is not set ++# CONFIG_SOFT_WATCHDOG is not set ++# CONFIG_UML_WATCHDOG is not set ++CONFIG_UML_SOUND=y ++CONFIG_SOUND=y ++CONFIG_HOSTAUDIO=y ++# CONFIG_TTY_LOG is not set ++ ++# ++# Block Devices ++# ++CONFIG_BLK_DEV_UBD=y ++# CONFIG_BLK_DEV_UBD_SYNC is not set ++# CONFIG_COW is not set ++CONFIG_COW_COMMON=y ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_NBD=y ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++CONFIG_BLK_DEV_INITRD=y ++# CONFIG_MMAPPER is not set ++CONFIG_NETDEVICES=y ++ ++# ++# Network Devices ++# ++CONFIG_UML_NET=y ++CONFIG_UML_NET_ETHERTAP=y ++CONFIG_UML_NET_TUNTAP=y ++CONFIG_UML_NET_SLIP=y ++CONFIG_UML_NET_SLIRP=y ++CONFIG_UML_NET_DAEMON=y ++CONFIG_UML_NET_MCAST=y ++# CONFIG_UML_NET_PCAP is not set ++CONFIG_DUMMY=y ++# CONFIG_BONDING is not set ++# CONFIG_EQUALIZER is not set ++CONFIG_TUN=y ++CONFIG_PPP=y ++# CONFIG_PPP_MULTILINK is not set ++# CONFIG_PPP_FILTER is not set ++# CONFIG_PPP_ASYNC is not set ++# CONFIG_PPP_SYNC_TTY is not set ++# CONFIG_PPP_DEFLATE is not set ++# CONFIG_PPP_BSDCOMP is not set ++# CONFIG_PPPOE is not set ++# CONFIG_PPP_MPPE is not set ++CONFIG_SLIP=y ++# CONFIG_SLIP_COMPRESSED is not set ++# CONFIG_SLIP_SMART is not set ++# CONFIG_SLIP_MODE_SLIP6 is not set ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_PACKET_MMAP=y ++# CONFIG_NETLINK_DEV is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_FILTER is not set ++CONFIG_UNIX=y ++CONFIG_INET=y ++# CONFIG_IP_MULTICAST is not set ++# CONFIG_IP_ADVANCED_ROUTER is not set ++# CONFIG_IP_PNP is not set ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE is not set ++# CONFIG_ARPD is not set ++# CONFIG_INET_ECN is not set ++# CONFIG_SYN_COOKIES is not set ++# CONFIG_IPV6 is not set ++# CONFIG_KHTTPD is not set ++ ++# ++# SCTP Configuration (EXPERIMENTAL) ++# ++CONFIG_IPV6_SCTP__=y ++# CONFIG_IP_SCTP is not set ++# CONFIG_ATM is not set ++# CONFIG_VLAN_8021Q is not set ++ ++# ++# ++# ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++ ++# ++# Appletalk devices ++# ++# CONFIG_DEV_APPLETALK is not set ++# CONFIG_DECNET is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_LLC is not set ++# CONFIG_NET_DIVERT is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_NET_FASTROUTE is not set ++# CONFIG_NET_HW_FLOWCONTROL is not set ++ ++# ++# QoS and/or fair queueing ++# ++# CONFIG_NET_SCHED is not set ++ ++# ++# Network testing ++# ++# CONFIG_NET_PKTGEN is not set ++ ++# ++# File systems ++# ++CONFIG_QUOTA=y ++# CONFIG_QFMT_V2 is not set ++CONFIG_AUTOFS_FS=y ++CONFIG_AUTOFS4_FS=y ++CONFIG_REISERFS_FS=y ++# CONFIG_REISERFS_CHECK is not set ++# CONFIG_REISERFS_PROC_INFO is not set ++# CONFIG_ADFS_FS is not set ++# CONFIG_ADFS_FS_RW is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BEFS_DEBUG is not set ++# CONFIG_BFS_FS is not set ++CONFIG_EXT3_FS=y ++CONFIG_JBD=y ++# CONFIG_JBD_DEBUG is not set ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_UMSDOS_FS=y ++CONFIG_VFAT_FS=y ++# CONFIG_EFS_FS is not set ++CONFIG_JFFS_FS=y ++CONFIG_JFFS_FS_VERBOSE=0 ++CONFIG_JFFS_PROC_FS=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_FS_DEBUG=0 ++# CONFIG_CRAMFS is not set ++CONFIG_TMPFS=y ++CONFIG_RAMFS=y ++CONFIG_ISO9660_FS=y ++# CONFIG_JOLIET is not set ++# CONFIG_ZISOFS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_JFS_DEBUG is not set ++# CONFIG_JFS_STATISTICS is not set ++CONFIG_MINIX_FS=y ++# CONFIG_VXFS_FS is not set ++# CONFIG_NTFS_FS is not set ++# CONFIG_NTFS_RW is not set ++# CONFIG_HPFS_FS is not set ++CONFIG_PROC_FS=y ++CONFIG_DEVFS_FS=y ++CONFIG_DEVFS_MOUNT=y ++# CONFIG_DEVFS_DEBUG is not set ++CONFIG_DEVPTS_FS=y ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_QNX4FS_RW is not set ++# CONFIG_ROMFS_FS is not set ++CONFIG_EXT2_FS=y ++# CONFIG_SYSV_FS is not set ++# CONFIG_UDF_FS is not set ++# CONFIG_UDF_RW is not set ++# CONFIG_UFS_FS is not set ++# CONFIG_UFS_FS_WRITE is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_XFS_QUOTA is not set ++# CONFIG_XFS_RT is not set ++# CONFIG_XFS_TRACE is not set ++# CONFIG_XFS_DEBUG is not set ++ ++# ++# Network File Systems ++# ++# CONFIG_CODA_FS is not set ++# CONFIG_INTERMEZZO_FS is not set ++# CONFIG_NFS_FS is not set ++# CONFIG_NFS_V3 is not set ++# CONFIG_NFS_DIRECTIO is not set ++# CONFIG_ROOT_NFS is not set ++# CONFIG_NFSD is not set ++# CONFIG_NFSD_V3 is not set ++# CONFIG_NFSD_TCP is not set ++# CONFIG_SUNRPC is not set ++# CONFIG_LOCKD is not set ++# CONFIG_SMB_FS is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_NCPFS_PACKET_SIGNING is not set ++# CONFIG_NCPFS_IOCTL_LOCKING is not set ++# CONFIG_NCPFS_STRONG is not set ++# CONFIG_NCPFS_NFS_NS is not set ++# CONFIG_NCPFS_OS2_NS is not set ++# CONFIG_NCPFS_SMALLDOS is not set ++# CONFIG_NCPFS_NLS is not set ++# CONFIG_NCPFS_EXTRAS is not set ++# CONFIG_ZISOFS_FS is not set ++ ++# ++# Partition Types ++# ++# CONFIG_PARTITION_ADVANCED is not set ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_SMB_NLS is not set ++CONFIG_NLS=y ++ ++# ++# Native Language Support ++# ++CONFIG_NLS_DEFAULT="iso8859-1" ++# CONFIG_NLS_CODEPAGE_437 is not set ++# CONFIG_NLS_CODEPAGE_737 is not set ++# CONFIG_NLS_CODEPAGE_775 is not set ++# CONFIG_NLS_CODEPAGE_850 is not set ++# CONFIG_NLS_CODEPAGE_852 is not set ++# CONFIG_NLS_CODEPAGE_855 is not set ++# CONFIG_NLS_CODEPAGE_857 is not set ++# CONFIG_NLS_CODEPAGE_860 is not set ++# CONFIG_NLS_CODEPAGE_861 is not set ++# CONFIG_NLS_CODEPAGE_862 is not set ++# CONFIG_NLS_CODEPAGE_863 is not set ++# CONFIG_NLS_CODEPAGE_864 is not set ++# CONFIG_NLS_CODEPAGE_865 is not set ++# CONFIG_NLS_CODEPAGE_866 is not set ++# CONFIG_NLS_CODEPAGE_869 is not set ++# CONFIG_NLS_CODEPAGE_936 is not set ++# CONFIG_NLS_CODEPAGE_950 is not set ++# CONFIG_NLS_CODEPAGE_932 is not set ++# CONFIG_NLS_CODEPAGE_949 is not set ++# CONFIG_NLS_CODEPAGE_874 is not set ++# CONFIG_NLS_ISO8859_8 is not set ++# CONFIG_NLS_CODEPAGE_1250 is not set ++# CONFIG_NLS_CODEPAGE_1251 is not set ++# CONFIG_NLS_ISO8859_1 is not set ++# CONFIG_NLS_ISO8859_2 is not set ++# CONFIG_NLS_ISO8859_3 is not set ++# CONFIG_NLS_ISO8859_4 is not set ++# CONFIG_NLS_ISO8859_5 is not set ++# CONFIG_NLS_ISO8859_6 is not set ++# CONFIG_NLS_ISO8859_7 is not set ++# CONFIG_NLS_ISO8859_9 is not set ++# CONFIG_NLS_ISO8859_13 is not set ++# CONFIG_NLS_ISO8859_14 is not set ++# CONFIG_NLS_ISO8859_15 is not set ++# CONFIG_NLS_KOI8_R is not set ++# CONFIG_NLS_KOI8_U is not set ++# CONFIG_NLS_UTF8 is not set ++ ++# ++# SCSI support ++# ++CONFIG_SCSI=y ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++# CONFIG_BLK_DEV_SD is not set ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_BLK_DEV_SR is not set ++# CONFIG_CHR_DEV_SG is not set ++ ++# ++# Some SCSI devices (e.g. CD jukebox) support multiple LUNs ++# ++# CONFIG_SCSI_DEBUG_QUEUES is not set ++# CONFIG_SCSI_MULTI_LUN is not set ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++CONFIG_SCSI_DEBUG=y ++ ++# ++# Multi-device support (RAID and LVM) ++# ++# CONFIG_MD is not set ++# CONFIG_BLK_DEV_MD is not set ++# CONFIG_MD_LINEAR is not set ++# CONFIG_MD_RAID0 is not set ++# CONFIG_MD_RAID1 is not set ++# CONFIG_MD_RAID5 is not set ++# CONFIG_MD_MULTIPATH is not set ++# CONFIG_BLK_DEV_LVM is not set ++ ++# ++# Memory Technology Devices (MTD) ++# ++CONFIG_MTD=y ++# CONFIG_MTD_DEBUG is not set ++# CONFIG_MTD_PARTITIONS is not set ++# CONFIG_MTD_CONCAT is not set ++# CONFIG_MTD_REDBOOT_PARTS is not set ++# CONFIG_MTD_CMDLINE_PARTS is not set ++ ++# ++# User Modules And Translation Layers ++# ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++# CONFIG_FTL is not set ++# CONFIG_NFTL is not set ++ ++# ++# RAM/ROM/Flash chip drivers ++# ++# CONFIG_MTD_CFI is not set ++# CONFIG_MTD_JEDECPROBE is not set ++# CONFIG_MTD_GEN_PROBE is not set ++# CONFIG_MTD_CFI_INTELEXT is not set ++# CONFIG_MTD_CFI_AMDSTD is not set ++# CONFIG_MTD_CFI_STAA is not set ++# CONFIG_MTD_RAM is not set ++# CONFIG_MTD_ROM is not set ++# CONFIG_MTD_ABSENT is not set ++# CONFIG_MTD_OBSOLETE_CHIPS is not set ++# CONFIG_MTD_AMDSTD is not set ++# CONFIG_MTD_SHARP is not set ++# CONFIG_MTD_JEDEC is not set ++ ++# ++# Mapping drivers for chip access ++# ++# CONFIG_MTD_PHYSMAP is not set ++# CONFIG_MTD_PCI is not set ++# CONFIG_MTD_PCMCIA is not set ++ ++# ++# Self-contained MTD device drivers ++# ++# CONFIG_MTD_PMC551 is not set ++# CONFIG_MTD_SLRAM is not set ++# CONFIG_MTD_MTDRAM is not set ++CONFIG_MTD_BLKMTD=y ++ ++# ++# Disk-On-Chip Device Drivers ++# ++# CONFIG_MTD_DOC1000 is not set ++# CONFIG_MTD_DOC2000 is not set ++# CONFIG_MTD_DOC2001 is not set ++# CONFIG_MTD_DOCPROBE is not set ++ ++# ++# NAND Flash Device Drivers ++# ++# CONFIG_MTD_NAND is not set ++ ++# ++# Library routines ++# ++# CONFIG_CRC32 is not set ++CONFIG_ZLIB_INFLATE=y ++CONFIG_ZLIB_DEFLATE=y ++ ++# ++# Kernel hacking ++# ++# CONFIG_DEBUG_SLAB is not set ++CONFIG_DEBUGSYM=y ++CONFIG_PT_PROXY=y ++# CONFIG_GCOV is not set +Index: linux-2.4.29/arch/um/drivers/chan_kern.c +=================================================================== +--- linux-2.4.29.orig/arch/um/drivers/chan_kern.c 1970-01-01 03:00:00.000000000 +0300 ++++ linux-2.4.29/arch/um/drivers/chan_kern.c 2005-05-03 22:28:14.196452024 +0300 +@@ -0,0 +1,568 @@ ++/* ++ * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) ++ * Licensed under the GPL ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#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 , 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 ++ * ++ * Software only watchdog driver. Unlike its big brother the WDT501P ++ * driver this won't always recover a failed machine. ++ * ++ * 03/96: Angelo Haritsis : ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#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=\n" DSP_HELP); ++ ++static int set_mixer(char *name, int *add) ++{ ++ mixer = name; ++ return(0); ++} ++ ++__uml_setup("mixer=", set_mixer, "mixer=\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 ++ * ++ * 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#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 = - Add a new device to UML; \n\ ++ same syntax as command line \n\ ++ config - Query the configuration of a device \n\ ++ remove - Remove a device from UML \n\ ++ sysrq - 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 - make UML enter into the kernel log\n\ ++ proc - returns the contents of the UML's /proc/\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:\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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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;irequest.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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 "); ++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]+=,\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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#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 ++ * 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 ++ * Licensed under the GPL. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#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" ++" 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;ifd); ++ 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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=,,<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, ++}; diff --git a/lustre/kernel_patches/patches/vfs_intent-2.4.21-rhel.patch b/lustre/kernel_patches/patches/vfs_intent-2.4.21-rhel.patch index 1da088c..1055dee 100644 --- a/lustre/kernel_patches/patches/vfs_intent-2.4.21-rhel.patch +++ b/lustre/kernel_patches/patches/vfs_intent-2.4.21-rhel.patch @@ -14,8 +14,8 @@ Index: linux-2.4.21/fs/dcache.c =================================================================== ---- linux-2.4.21.orig/fs/dcache.c 2004-09-11 10:16:31.000000000 -0400 -+++ linux-2.4.21/fs/dcache.c 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/fs/dcache.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/fs/dcache.c 2005-06-01 22:58:09.043064136 -0400 @@ -187,6 +187,13 @@ spin_unlock(&dcache_lock); return 0; @@ -55,8 +55,8 @@ Index: linux-2.4.21/fs/dcache.c #define do_switch(x,y) do { \ Index: linux-2.4.21/fs/exec.c =================================================================== ---- linux-2.4.21.orig/fs/exec.c 2004-09-11 10:16:39.000000000 -0400 -+++ linux-2.4.21/fs/exec.c 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/fs/exec.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/fs/exec.c 2005-06-01 22:58:09.044063984 -0400 @@ -116,8 +116,10 @@ struct file * file; struct nameidata nd; @@ -79,7 +79,7 @@ Index: linux-2.4.21/fs/exec.c error = PTR_ERR(file); if (IS_ERR(file)) goto out; -@@ -401,8 +404,10 @@ +@@ -405,8 +408,10 @@ struct inode *inode; struct file *file; int err = 0; @@ -91,7 +91,7 @@ Index: linux-2.4.21/fs/exec.c file = ERR_PTR(err); if (!err) { inode = nd.dentry->d_inode; -@@ -414,7 +419,8 @@ +@@ -418,7 +423,8 @@ err = -EACCES; file = ERR_PTR(err); if (!err) { @@ -101,7 +101,7 @@ Index: linux-2.4.21/fs/exec.c if (!IS_ERR(file)) { err = deny_write_access(file); if (err) { -@@ -426,6 +432,7 @@ +@@ -430,6 +436,7 @@ return file; } } @@ -109,7 +109,7 @@ Index: linux-2.4.21/fs/exec.c path_release(&nd); } goto out; -@@ -1355,7 +1362,7 @@ +@@ -1368,7 +1375,7 @@ goto close_fail; if (!file->f_op->write) goto close_fail; @@ -120,8 +120,8 @@ Index: linux-2.4.21/fs/exec.c retval = binfmt->core_dump(signr, regs, file); Index: linux-2.4.21/fs/namei.c =================================================================== ---- linux-2.4.21.orig/fs/namei.c 2004-09-11 10:16:29.000000000 -0400 -+++ linux-2.4.21/fs/namei.c 2004-09-16 19:34:57.000000000 -0400 +--- linux-2.4.21.orig/fs/namei.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/fs/namei.c 2005-06-01 23:01:30.065504080 -0400 @@ -94,6 +94,13 @@ * XEmacs seems to be relying on it... */ @@ -248,16 +248,16 @@ Index: linux-2.4.21/fs/namei.c err = PTR_ERR(dentry); if (IS_ERR(dentry)) break; -@@ -547,7 +583,7 @@ - goto out_dput; +@@ -548,7 +584,7 @@ if (inode->i_op->follow_link) { + struct vfsmount *mnt = mntget(nd->mnt); - err = do_follow_link(dentry, nd); + err = do_follow_link(dentry, nd, NULL); dput(dentry); + mntput(mnt); if (err) - goto return_err; -@@ -563,7 +599,7 @@ +@@ -565,7 +601,7 @@ nd->dentry = dentry; } err = -ENOTDIR; @@ -266,7 +266,7 @@ Index: linux-2.4.21/fs/namei.c break; continue; /* here ends the main loop */ -@@ -590,12 +626,12 @@ +@@ -592,12 +628,12 @@ if (err < 0) break; } @@ -281,16 +281,16 @@ Index: linux-2.4.21/fs/namei.c err = PTR_ERR(dentry); if (IS_ERR(dentry)) break; -@@ -605,7 +641,7 @@ - inode = dentry->d_inode; +@@ -608,7 +644,7 @@ if ((lookup_flags & LOOKUP_FOLLOW) && inode && inode->i_op && inode->i_op->follow_link) { + struct vfsmount *mnt = mntget(nd->mnt); - err = do_follow_link(dentry, nd); + err = do_follow_link(dentry, nd, it); dput(dentry); + mntput(mnt); if (err) - goto return_err; -@@ -619,7 +655,8 @@ +@@ -623,7 +659,8 @@ goto no_inode; if (lookup_flags & LOOKUP_DIRECTORY) { err = -ENOTDIR; @@ -300,7 +300,7 @@ Index: linux-2.4.21/fs/namei.c break; } goto return_base; -@@ -637,12 +672,42 @@ return_reval: +@@ -641,12 +678,42 @@ nd->last_type = LAST_DOT; else if (this.len == 2 && this.name[1] == '.') nd->last_type = LAST_DOTDOT; @@ -343,7 +343,7 @@ Index: linux-2.4.21/fs/namei.c if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { err = -ESTALE; if (!dentry->d_op->d_revalidate(dentry, 0)) { -@@ -656,15 +721,28 @@ +@@ -660,15 +727,28 @@ dput(dentry); break; } @@ -373,7 +373,7 @@ Index: linux-2.4.21/fs/namei.c } /* SMP-safe */ -@@ -749,6 +827,17 @@ +@@ -753,6 +833,17 @@ } /* SMP-safe */ @@ -391,7 +391,7 @@ Index: linux-2.4.21/fs/namei.c int path_lookup(const char *path, unsigned flags, struct nameidata *nd) { int error = 0; -@@ -763,6 +852,7 @@ +@@ -767,6 +858,7 @@ { nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags; @@ -399,7 +399,7 @@ Index: linux-2.4.21/fs/namei.c if (*name=='/') return walk_init_root(name,nd); read_lock(¤t->fs->lock); -@@ -777,7 +867,8 @@ +@@ -781,7 +873,8 @@ * needs parent already locked. Doesn't follow mounts. * SMP-safe. */ @@ -409,7 +409,7 @@ Index: linux-2.4.21/fs/namei.c { struct dentry * dentry; struct inode *inode; -@@ -800,13 +891,16 @@ +@@ -804,13 +897,16 @@ goto out; } @@ -427,7 +427,7 @@ Index: linux-2.4.21/fs/namei.c dentry = inode->i_op->lookup(inode, new); unlock_kernel(); if (!dentry) -@@ -818,6 +912,12 @@ +@@ -822,6 +918,12 @@ return dentry; } @@ -440,7 +440,7 @@ Index: linux-2.4.21/fs/namei.c /* SMP-safe */ struct dentry * lookup_one_len(const char * name, struct dentry * base, int len) { -@@ -839,7 +939,7 @@ +@@ -843,7 +945,7 @@ } this.hash = end_name_hash(hash); @@ -449,7 +449,7 @@ Index: linux-2.4.21/fs/namei.c access: return ERR_PTR(-EACCES); } -@@ -870,6 +970,23 @@ +@@ -874,6 +976,23 @@ return err; } @@ -473,7 +473,7 @@ Index: linux-2.4.21/fs/namei.c /* * It's inline, so penalty for filesystems that don't use sticky bit is * minimal. -@@ -967,7 +1084,8 @@ +@@ -971,7 +1090,8 @@ return retval; } @@ -483,7 +483,7 @@ Index: linux-2.4.21/fs/namei.c { int error; -@@ -980,12 +1098,15 @@ +@@ -984,12 +1104,15 @@ goto exit_lock; error = -EACCES; /* shouldn't it be ENOSYS? */ @@ -501,7 +501,7 @@ Index: linux-2.4.21/fs/namei.c unlock_kernel(); exit_lock: up(&dir->i_zombie); -@@ -994,6 +1115,11 @@ +@@ -998,6 +1121,11 @@ return error; } @@ -513,7 +513,7 @@ Index: linux-2.4.21/fs/namei.c /* * open_namei() * -@@ -1008,7 +1134,8 @@ +@@ -1012,7 +1140,8 @@ * for symlinks (where the permissions are checked later). * SMP-safe */ @@ -523,7 +523,7 @@ Index: linux-2.4.21/fs/namei.c { int acc_mode, error = 0; struct inode *inode; -@@ -1018,11 +1145,14 @@ +@@ -1023,11 +1152,14 @@ acc_mode = ACC_MODE(flag); @@ -539,7 +539,7 @@ Index: linux-2.4.21/fs/namei.c if (error) return error; dentry = nd->dentry; -@@ -1032,6 +1162,10 @@ +@@ -1037,6 +1169,10 @@ /* * Create - we need to know the parent. */ @@ -550,7 +550,7 @@ Index: linux-2.4.21/fs/namei.c error = path_lookup(pathname, LOOKUP_PARENT, nd); if (error) return error; -@@ -1047,7 +1181,7 @@ +@@ -1052,7 +1188,7 @@ dir = nd->dentry; down(&dir->d_inode->i_sem); @@ -559,7 +559,7 @@ Index: linux-2.4.21/fs/namei.c do_last: error = PTR_ERR(dentry); -@@ -1056,11 +1190,12 @@ +@@ -1061,11 +1197,12 @@ goto exit; } @@ -573,7 +573,7 @@ Index: linux-2.4.21/fs/namei.c up(&dir->d_inode->i_sem); dput(nd->dentry); nd->dentry = dentry; -@@ -1164,7 +1299,7 @@ +@@ -1169,7 +1306,7 @@ if (!error) { DQUOT_INIT(inode); @@ -582,7 +582,7 @@ Index: linux-2.4.21/fs/namei.c } put_write_access(inode); if (error) -@@ -1176,8 +1311,10 @@ +@@ -1181,8 +1318,10 @@ return 0; exit_dput: @@ -593,18 +593,18 @@ Index: linux-2.4.21/fs/namei.c path_release(nd); return error; -@@ -1196,7 +1333,10 @@ - * are done. Procfs-like symlinks just set LAST_BIND. +@@ -1202,7 +1341,10 @@ */ UPDATE_ATIME(dentry->d_inode); + mnt = mntget(nd->mnt); + nd->intent = it; error = dentry->d_inode->i_op->follow_link(dentry, nd); + if (error) + intent_release(it); dput(dentry); + mntput(mnt); if (error) - return error; -@@ -1218,13 +1358,20 @@ +@@ -1225,13 +1367,20 @@ } dir = nd->dentry; down(&dir->d_inode->i_sem); @@ -627,7 +627,7 @@ Index: linux-2.4.21/fs/namei.c { struct dentry *dentry; -@@ -1232,7 +1379,7 @@ +@@ -1239,7 +1388,7 @@ dentry = ERR_PTR(-EEXIST); if (nd->last_type != LAST_NORM) goto fail; @@ -636,7 +636,7 @@ Index: linux-2.4.21/fs/namei.c if (IS_ERR(dentry)) goto fail; if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode) -@@ -1288,7 +1435,20 @@ +@@ -1295,7 +1444,20 @@ error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) goto out; @@ -658,7 +658,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(dentry); if (!IS_POSIXACL(nd.dentry->d_inode)) -@@ -1310,6 +1470,7 @@ +@@ -1317,6 +1479,7 @@ dput(dentry); } up(&nd.dentry->d_inode->i_sem); @@ -666,7 +666,7 @@ Index: linux-2.4.21/fs/namei.c path_release(&nd); out: putname(tmp); -@@ -1357,7 +1518,18 @@ +@@ -1364,7 +1527,18 @@ error = path_lookup(tmp, LOOKUP_PARENT, &nd); if (error) goto out; @@ -686,7 +686,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { if (!IS_POSIXACL(nd.dentry->d_inode)) -@@ -1366,6 +1538,7 @@ +@@ -1373,6 +1547,7 @@ dput(dentry); } up(&nd.dentry->d_inode->i_sem); @@ -694,7 +694,7 @@ Index: linux-2.4.21/fs/namei.c path_release(&nd); out: putname(tmp); -@@ -1466,8 +1639,16 @@ +@@ -1473,8 +1648,16 @@ error = -EBUSY; goto exit1; } @@ -712,7 +712,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { error = vfs_rmdir(nd.dentry->d_inode, dentry); -@@ -1525,8 +1706,15 @@ +@@ -1532,8 +1715,15 @@ error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; @@ -729,7 +729,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { /* Why not before? Because we want correct error value */ -@@ -1593,15 +1781,27 @@ +@@ -1600,15 +1790,27 @@ error = path_lookup(to, LOOKUP_PARENT, &nd); if (error) goto out; @@ -759,7 +759,7 @@ Index: linux-2.4.21/fs/namei.c putname(to); } putname(from); -@@ -1677,7 +1877,18 @@ +@@ -1684,7 +1886,18 @@ error = -EXDEV; if (old_nd.mnt != nd.mnt) goto out_release; @@ -779,7 +779,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(new_dentry); if (!IS_ERR(new_dentry)) { error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry); -@@ -1721,7 +1932,7 @@ +@@ -1728,7 +1941,7 @@ * locking]. */ int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, @@ -788,7 +788,7 @@ Index: linux-2.4.21/fs/namei.c { int error; struct inode *target; -@@ -1800,7 +2011,7 @@ +@@ -1807,7 +2020,7 @@ } int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, @@ -797,7 +797,7 @@ Index: linux-2.4.21/fs/namei.c { int error; -@@ -1888,9 +2099,18 @@ +@@ -1895,9 +2108,18 @@ if (newnd.last_type != LAST_NORM) goto exit2; @@ -817,7 +817,7 @@ Index: linux-2.4.21/fs/namei.c error = PTR_ERR(old_dentry); if (IS_ERR(old_dentry)) goto exit3; -@@ -1906,16 +2126,16 @@ +@@ -1913,16 +2135,16 @@ if (newnd.last.name[newnd.last.len]) goto exit4; } @@ -836,7 +836,7 @@ Index: linux-2.4.21/fs/namei.c dput(new_dentry); exit4: dput(old_dentry); -@@ -1966,20 +2186,26 @@ +@@ -1973,20 +2195,26 @@ } static inline int @@ -865,7 +865,7 @@ Index: linux-2.4.21/fs/namei.c out: if (current->link_count || res || nd->last_type!=LAST_NORM) return res; -@@ -2003,7 +2229,13 @@ +@@ -2010,7 +2238,13 @@ int vfs_follow_link(struct nameidata *nd, const char *link) { @@ -880,7 +880,7 @@ Index: linux-2.4.21/fs/namei.c } /* get the link contents into pagecache */ -@@ -2045,7 +2277,7 @@ +@@ -2052,7 +2286,7 @@ { struct page *page = NULL; char *s = page_getlink(dentry, &page); @@ -891,8 +891,8 @@ Index: linux-2.4.21/fs/namei.c page_cache_release(page); Index: linux-2.4.21/fs/namespace.c =================================================================== ---- linux-2.4.21.orig/fs/namespace.c 2004-09-11 10:16:15.000000000 -0400 -+++ linux-2.4.21/fs/namespace.c 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/fs/namespace.c 2005-06-01 22:51:42.000000000 -0400 ++++ linux-2.4.21/fs/namespace.c 2005-06-01 22:58:09.048063376 -0400 @@ -98,6 +98,7 @@ { old_nd->dentry = mnt->mnt_mountpoint; @@ -1007,8 +1007,8 @@ Index: linux-2.4.21/fs/namespace.c unlock_kernel(); Index: linux-2.4.21/fs/open.c =================================================================== ---- linux-2.4.21.orig/fs/open.c 2004-09-11 10:16:19.000000000 -0400 -+++ linux-2.4.21/fs/open.c 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/fs/open.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/fs/open.c 2005-06-01 22:58:09.050063072 -0400 @@ -19,6 +19,8 @@ #include <asm/uaccess.h> @@ -1018,7 +1018,7 @@ Index: linux-2.4.21/fs/open.c int vfs_statfs(struct super_block *sb, struct statfs *buf) { -@@ -95,9 +97,10 @@ +@@ -169,9 +171,10 @@ write_unlock(&files->file_lock); } @@ -1030,7 +1030,7 @@ Index: linux-2.4.21/fs/open.c int error; struct iattr newattrs; -@@ -109,7 +112,13 @@ +@@ -183,7 +186,13 @@ down(&inode->i_sem); newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; @@ -1045,7 +1045,7 @@ Index: linux-2.4.21/fs/open.c up(&inode->i_sem); up_write(&inode->i_alloc_sem); return error; -@@ -120,12 +129,13 @@ +@@ -194,12 +203,13 @@ struct nameidata nd; struct inode * inode; int error; @@ -1060,7 +1060,7 @@ Index: linux-2.4.21/fs/open.c if (error) goto out; inode = nd.dentry->d_inode; -@@ -165,11 +175,13 @@ +@@ -239,11 +249,13 @@ error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); @@ -1075,7 +1075,7 @@ Index: linux-2.4.21/fs/open.c path_release(&nd); out: return error; -@@ -217,7 +229,7 @@ +@@ -291,7 +303,7 @@ error = locks_verify_truncate(inode, file, length); if (!error) @@ -1084,7 +1084,7 @@ Index: linux-2.4.21/fs/open.c out_putf: fput(file); out: -@@ -262,11 +274,13 @@ +@@ -336,11 +348,13 @@ struct inode * inode; struct iattr newattrs; @@ -1099,7 +1099,7 @@ Index: linux-2.4.21/fs/open.c error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; -@@ -281,11 +295,25 @@ +@@ -355,11 +369,25 @@ goto dput_and_out; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; @@ -1126,7 +1126,7 @@ Index: linux-2.4.21/fs/open.c error = notify_change(nd.dentry, &newattrs); dput_and_out: path_release(&nd); -@@ -306,12 +334,14 @@ +@@ -380,12 +408,14 @@ struct inode * inode; struct iattr newattrs; @@ -1142,7 +1142,7 @@ Index: linux-2.4.21/fs/open.c error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; -@@ -326,7 +356,20 @@ +@@ -400,7 +430,20 @@ newattrs.ia_atime = times[0].tv_sec; newattrs.ia_mtime = times[1].tv_sec; newattrs.ia_valid |= ATTR_ATIME_SET | ATTR_MTIME_SET; @@ -1164,7 +1164,7 @@ Index: linux-2.4.21/fs/open.c if (current->fsuid != inode->i_uid && (error = permission(inode,MAY_WRITE)) != 0) goto dput_and_out; -@@ -349,6 +392,7 @@ +@@ -423,6 +466,7 @@ int old_fsuid, old_fsgid; kernel_cap_t old_cap; int res; @@ -1172,7 +1172,7 @@ Index: linux-2.4.21/fs/open.c if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */ return -EINVAL; -@@ -366,13 +410,14 @@ +@@ -440,13 +484,14 @@ else current->cap_effective = current->cap_permitted; @@ -1188,7 +1188,7 @@ Index: linux-2.4.21/fs/open.c path_release(&nd); } -@@ -387,8 +432,9 @@ +@@ -461,8 +506,9 @@ { int error; struct nameidata nd; @@ -1199,7 +1199,7 @@ Index: linux-2.4.21/fs/open.c if (error) goto out; -@@ -399,6 +445,7 @@ +@@ -473,6 +519,7 @@ set_fs_pwd(current->fs, nd.mnt, nd.dentry); dput_and_out: @@ -1207,7 +1207,7 @@ Index: linux-2.4.21/fs/open.c path_release(&nd); out: return error; -@@ -438,9 +485,10 @@ +@@ -512,9 +559,10 @@ { int error; struct nameidata nd; @@ -1220,7 +1220,7 @@ Index: linux-2.4.21/fs/open.c if (error) goto out; -@@ -456,39 +504,56 @@ +@@ -530,39 +578,56 @@ set_fs_altroot(); error = 0; dput_and_out: @@ -1291,7 +1291,7 @@ Index: linux-2.4.21/fs/open.c fput(file); out: return err; -@@ -497,30 +562,14 @@ +@@ -571,30 +636,14 @@ asmlinkage long sys_chmod(const char * filename, mode_t mode) { struct nameidata nd; @@ -1307,23 +1307,23 @@ Index: linux-2.4.21/fs/open.c - error = -EROFS; - if (IS_RDONLY(inode)) - goto dput_and_out; - +- - error = -EPERM; - if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) - goto dput_and_out; -+ error = chmod_common(nd.dentry, mode); - if (mode == (mode_t) -1) - mode = inode->i_mode; - newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - error = notify_change(nd.dentry, &newattrs); -- ++ error = chmod_common(nd.dentry, mode); + -dput_and_out: path_release(&nd); out: return error; -@@ -540,6 +589,20 @@ +@@ -614,6 +663,20 @@ error = -EROFS; if (IS_RDONLY(inode)) goto out; @@ -1344,7 +1344,7 @@ Index: linux-2.4.21/fs/open.c error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto out; -@@ -644,6 +707,7 @@ +@@ -718,6 +781,7 @@ { int namei_flags, error; struct nameidata nd; @@ -1352,7 +1352,7 @@ Index: linux-2.4.21/fs/open.c namei_flags = flags; if ((namei_flags+1) & O_ACCMODE) -@@ -651,14 +715,15 @@ +@@ -725,14 +789,15 @@ if (namei_flags & O_TRUNC) namei_flags |= 2; @@ -1373,7 +1373,7 @@ Index: linux-2.4.21/fs/open.c { struct file * f; struct inode *inode; -@@ -695,7 +760,9 @@ +@@ -769,7 +834,9 @@ } if (f->f_op && f->f_op->open) { @@ -1383,7 +1383,7 @@ Index: linux-2.4.21/fs/open.c if (error) goto cleanup_all; } -@@ -711,6 +778,7 @@ +@@ -785,6 +852,7 @@ } } @@ -1391,7 +1391,7 @@ Index: linux-2.4.21/fs/open.c return f; cleanup_all: -@@ -725,11 +793,17 @@ +@@ -799,11 +867,17 @@ cleanup_file: put_filp(f); cleanup_dentry: @@ -1411,8 +1411,8 @@ Index: linux-2.4.21/fs/open.c */ Index: linux-2.4.21/fs/stat.c =================================================================== ---- linux-2.4.21.orig/fs/stat.c 2004-09-11 10:16:30.000000000 -0400 -+++ linux-2.4.21/fs/stat.c 2004-09-16 19:38:49.000000000 -0400 +--- linux-2.4.21.orig/fs/stat.c 2005-06-01 22:51:55.000000000 -0400 ++++ linux-2.4.21/fs/stat.c 2005-06-01 22:58:09.051062920 -0400 @@ -17,10 +17,12 @@ * Revalidate the inode. This is required for proper NFS attribute caching. */ @@ -1580,8 +1580,8 @@ Index: linux-2.4.21/fs/stat.c fput(f); Index: linux-2.4.21/include/linux/dcache.h =================================================================== ---- linux-2.4.21.orig/include/linux/dcache.h 2004-09-11 10:16:39.000000000 -0400 -+++ linux-2.4.21/include/linux/dcache.h 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/include/linux/dcache.h 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/include/linux/dcache.h 2005-06-01 22:58:09.051062920 -0400 @@ -6,6 +6,51 @@ #include <asm/atomic.h> #include <linux/mount.h> @@ -1667,8 +1667,8 @@ Index: linux-2.4.21/include/linux/dcache.h Index: linux-2.4.21/include/linux/fs.h =================================================================== ---- linux-2.4.21.orig/include/linux/fs.h 2004-09-16 19:21:00.000000000 -0400 -+++ linux-2.4.21/include/linux/fs.h 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/include/linux/fs.h 2005-06-01 22:57:59.887456000 -0400 ++++ linux-2.4.21/include/linux/fs.h 2005-06-01 22:58:09.053062616 -0400 @@ -73,6 +73,7 @@ #define FMODE_READ 1 @@ -1677,7 +1677,7 @@ Index: linux-2.4.21/include/linux/fs.h #define READ 0 #define WRITE 1 -@@ -362,6 +363,9 @@ +@@ -365,6 +366,9 @@ #define ATTR_MTIME_SET 256 #define ATTR_FORCE 512 /* Not a change, but a change it */ #define ATTR_ATTR_FLAG 1024 @@ -1687,7 +1687,7 @@ Index: linux-2.4.21/include/linux/fs.h /* * This is the Inode Attributes structure, used for notify_change(). It -@@ -499,6 +503,7 @@ +@@ -502,6 +506,7 @@ struct pipe_inode_info *i_pipe; struct block_device *i_bdev; struct char_device *i_cdev; @@ -1695,7 +1695,7 @@ Index: linux-2.4.21/include/linux/fs.h unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ -@@ -601,6 +606,7 @@ +@@ -604,6 +609,7 @@ /* needed for tty driver, and maybe others */ void *private_data; @@ -1703,7 +1703,7 @@ Index: linux-2.4.21/include/linux/fs.h /* preallocated helper kiobuf to speedup O_DIRECT */ struct kiobuf *f_iobuf; -@@ -729,6 +735,7 @@ +@@ -736,6 +742,7 @@ struct qstr last; unsigned int flags; int last_type; @@ -1711,7 +1711,7 @@ Index: linux-2.4.21/include/linux/fs.h }; /* -@@ -849,7 +856,8 @@ +@@ -856,7 +863,8 @@ extern int vfs_link(struct dentry *, struct inode *, struct dentry *); extern int vfs_rmdir(struct inode *, struct dentry *); extern int vfs_unlink(struct inode *, struct dentry *); @@ -1721,7 +1721,7 @@ Index: linux-2.4.21/include/linux/fs.h /* * File types -@@ -928,21 +936,32 @@ +@@ -935,21 +943,32 @@ struct inode_operations { int (*create) (struct inode *,struct dentry *,int); @@ -1754,7 +1754,7 @@ Index: linux-2.4.21/include/linux/fs.h int (*getattr) (struct dentry *, struct iattr *); int (*setxattr) (struct dentry *, const char *, const void *, size_t, int); ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t); -@@ -1139,10 +1158,14 @@ +@@ -1151,10 +1170,14 @@ asmlinkage long sys_open(const char *, int, int); asmlinkage long sys_close(unsigned int); /* yes, it's really unsigned */ @@ -1770,7 +1770,7 @@ Index: linux-2.4.21/include/linux/fs.h extern int filp_close(struct file *, fl_owner_t id); extern char * getname(const char *); -@@ -1434,6 +1457,7 @@ +@@ -1446,6 +1469,7 @@ extern loff_t default_llseek(struct file *file, loff_t offset, int origin); extern int FASTCALL(__user_walk(const char *, unsigned, struct nameidata *)); @@ -1778,7 +1778,7 @@ Index: linux-2.4.21/include/linux/fs.h extern int FASTCALL(path_init(const char *, unsigned, struct nameidata *)); extern int FASTCALL(path_walk(const char *, struct nameidata *)); extern int FASTCALL(path_lookup(const char *, unsigned, struct nameidata *)); -@@ -1445,6 +1469,8 @@ +@@ -1457,6 +1481,8 @@ extern struct dentry * lookup_hash(struct qstr *, struct dentry *); #define user_path_walk(name,nd) __user_walk(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, nd) #define user_path_walk_link(name,nd) __user_walk(name, LOOKUP_POSITIVE, nd) @@ -1787,7 +1787,7 @@ Index: linux-2.4.21/include/linux/fs.h extern void inode_init_once(struct inode *); extern void iput(struct inode *); -@@ -1609,6 +1635,8 @@ +@@ -1625,6 +1651,8 @@ extern int vfs_readlink(struct dentry *, char *, int, const char *); extern int vfs_follow_link(struct nameidata *, const char *); @@ -1798,8 +1798,8 @@ Index: linux-2.4.21/include/linux/fs.h extern struct inode_operations page_symlink_inode_operations; Index: linux-2.4.21/include/linux/fs_struct.h =================================================================== ---- linux-2.4.21.orig/include/linux/fs_struct.h 2004-09-11 10:16:14.000000000 -0400 -+++ linux-2.4.21/include/linux/fs_struct.h 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/include/linux/fs_struct.h 2005-06-01 22:51:40.000000000 -0400 ++++ linux-2.4.21/include/linux/fs_struct.h 2005-06-01 22:58:09.054062464 -0400 @@ -37,10 +37,12 @@ write_lock(&fs->lock); old_root = fs->root; @@ -1828,8 +1828,8 @@ Index: linux-2.4.21/include/linux/fs_struct.h } Index: linux-2.4.21/kernel/exit.c =================================================================== ---- linux-2.4.21.orig/kernel/exit.c 2004-09-11 10:16:35.000000000 -0400 -+++ linux-2.4.21/kernel/exit.c 2004-09-16 19:32:11.000000000 -0400 +--- linux-2.4.21.orig/kernel/exit.c 2005-06-01 22:52:04.000000000 -0400 ++++ linux-2.4.21/kernel/exit.c 2005-06-01 22:58:09.055062312 -0400 @@ -367,11 +367,14 @@ { /* No need to hold fs->lock if we are killing it */ @@ -1847,9 +1847,9 @@ Index: linux-2.4.21/kernel/exit.c } Index: linux-2.4.21/kernel/fork.c =================================================================== ---- linux-2.4.21.orig/kernel/fork.c 2004-09-11 10:16:39.000000000 -0400 -+++ linux-2.4.21/kernel/fork.c 2004-09-16 19:32:11.000000000 -0400 -@@ -472,10 +472,13 @@ +--- linux-2.4.21.orig/kernel/fork.c 2005-06-01 22:52:05.000000000 -0400 ++++ linux-2.4.21/kernel/fork.c 2005-06-01 22:58:09.055062312 -0400 +@@ -473,10 +473,13 @@ fs->umask = old->umask; read_lock(&old->lock); fs->rootmnt = mntget(old->rootmnt); @@ -1865,9 +1865,9 @@ Index: linux-2.4.21/kernel/fork.c } else { Index: linux-2.4.21/kernel/ksyms.c =================================================================== ---- linux-2.4.21.orig/kernel/ksyms.c 2004-09-16 19:21:00.000000000 -0400 -+++ linux-2.4.21/kernel/ksyms.c 2004-09-16 19:32:11.000000000 -0400 -@@ -317,6 +317,7 @@ +--- linux-2.4.21.orig/kernel/ksyms.c 2005-06-01 22:57:59.891455392 -0400 ++++ linux-2.4.21/kernel/ksyms.c 2005-06-01 22:58:09.056062160 -0400 +@@ -318,6 +318,7 @@ EXPORT_SYMBOL(set_page_dirty); EXPORT_SYMBOL(vfs_readlink); EXPORT_SYMBOL(vfs_follow_link); diff --git a/lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch b/lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch new file mode 100644 index 0000000..f19fbd4 --- /dev/null +++ b/lustre/kernel_patches/patches/vfs_intent-2.4.29-vanilla.patch @@ -0,0 +1,1833 @@ +Index: linux-2.4.29/fs/dcache.c +=================================================================== +--- linux-2.4.29.orig/fs/dcache.c 2005-04-07 18:52:37.000000000 +0300 ++++ linux-2.4.29/fs/dcache.c 2005-04-07 19:14:06.000000000 +0300 +@@ -184,6 +184,13 @@ + spin_unlock(&dcache_lock); + return 0; + } ++ ++ /* network invalidation by Lustre */ ++ if (dentry->d_flags & DCACHE_LUSTRE_INVALID) { ++ spin_unlock(&dcache_lock); ++ return 0; ++ } ++ + /* + * Check whether to do a partial shrink_dcache + * to get rid of unused child entries. +@@ -836,13 +843,19 @@ + * Adds a dentry to the hash according to its name. + */ + +-void d_rehash(struct dentry * entry) ++void __d_rehash(struct dentry * entry, int lock) + { + struct list_head *list = d_hash(entry->d_parent, entry->d_name.hash); + if (!list_empty(&entry->d_hash)) BUG(); +- spin_lock(&dcache_lock); ++ if (lock) spin_lock(&dcache_lock); + list_add(&entry->d_hash, list); +- spin_unlock(&dcache_lock); ++ if (lock) spin_unlock(&dcache_lock); ++} ++EXPORT_SYMBOL(__d_rehash); ++ ++void d_rehash(struct dentry * entry) ++{ ++ __d_rehash(entry, 1); + } + + #define do_switch(x,y) do { \ +Index: linux-2.4.29/fs/exec.c +=================================================================== +--- linux-2.4.29.orig/fs/exec.c 2005-04-07 18:53:19.000000000 +0300 ++++ linux-2.4.29/fs/exec.c 2005-04-07 19:14:06.000000000 +0300 +@@ -112,8 +112,10 @@ + struct file * file; + struct nameidata nd; + int error; ++ struct lookup_intent it = { .it_op = IT_OPEN, ++ .it_flags = FMODE_READ|FMODE_EXEC }; + +- error = user_path_walk(library, &nd); ++ error = user_path_walk_it(library, &nd, &it); + if (error) + goto out; + +@@ -125,7 +127,8 @@ + if (error) + goto exit; + +- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it); ++ intent_release(&it); + error = PTR_ERR(file); + if (IS_ERR(file)) + goto out; +@@ -378,8 +381,10 @@ + struct inode *inode; + struct file *file; + int err = 0; ++ struct lookup_intent it = { .it_op = IT_OPEN, ++ .it_flags = FMODE_READ|FMODE_EXEC }; + +- err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd); ++ err = path_lookup_it(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd, &it); + file = ERR_PTR(err); + if (!err) { + inode = nd.dentry->d_inode; +@@ -391,7 +396,8 @@ + err = -EACCES; + file = ERR_PTR(err); + if (!err) { +- file = dentry_open(nd.dentry, nd.mnt, O_RDONLY); ++ file = dentry_open_it(nd.dentry, nd.mnt, O_RDONLY, &it); ++ intent_release(&it); + if (!IS_ERR(file)) { + err = deny_write_access(file); + if (err) { +@@ -403,6 +409,7 @@ + return file; + } + } ++ intent_release(&it); + path_release(&nd); + } + goto out; +@@ -1163,7 +1170,7 @@ + goto close_fail; + if (!file->f_op->write) + goto close_fail; +- if (do_truncate(file->f_dentry, 0) != 0) ++ if (do_truncate(file->f_dentry, 0, 0) != 0) + goto close_fail; + + retval = binfmt->core_dump(signr, regs, file); +Index: linux-2.4.29/fs/namei.c +=================================================================== +--- linux-2.4.29.orig/fs/namei.c 2005-04-07 18:53:14.000000000 +0300 ++++ linux-2.4.29/fs/namei.c 2005-05-03 17:23:44.139922792 +0300 +@@ -94,6 +94,13 @@ + * XEmacs seems to be relying on it... + */ + ++void intent_release(struct lookup_intent *it) ++{ ++ if (it && it->it_op_release) ++ it->it_op_release(it); ++ ++} ++ + /* In order to reduce some races, while at the same time doing additional + * checking and hopefully speeding things up, we copy filenames to the + * kernel data space before using them.. +@@ -260,10 +267,19 @@ + * Internal lookup() using the new generic dcache. + * SMP-safe + */ +-static struct dentry * cached_lookup(struct dentry * parent, struct qstr * name, int flags) ++static struct dentry *cached_lookup(struct dentry *parent, struct qstr *name, ++ int flags, struct lookup_intent *it) + { + struct dentry * dentry = d_lookup(parent, name); + ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) { ++ if (!dentry->d_op->d_revalidate_it(dentry, flags, it) && ++ !d_invalidate(dentry)) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ return dentry; ++ } else + if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { + if (!dentry->d_op->d_revalidate(dentry, flags) && !d_invalidate(dentry)) { + dput(dentry); +@@ -281,11 +297,15 @@ + * make sure that nobody added the entry to the dcache in the meantime.. + * SMP-safe + */ +-static struct dentry * real_lookup(struct dentry * parent, struct qstr * name, int flags) ++static struct dentry *real_lookup(struct dentry *parent, struct qstr *name, ++ int flags, struct lookup_intent *it) + { + struct dentry * result; + struct inode *dir = parent->d_inode; ++ int counter = 0; + ++again: ++ counter++; + down(&dir->i_sem); + /* + * First re-do the cached lookup just in case it was created +@@ -300,6 +320,9 @@ + result = ERR_PTR(-ENOMEM); + if (dentry) { + lock_kernel(); ++ if (dir->i_op->lookup_it) ++ result = dir->i_op->lookup_it(dir, dentry, it, flags); ++ else + result = dir->i_op->lookup(dir, dentry); + unlock_kernel(); + if (result) +@@ -321,6 +344,15 @@ + dput(result); + result = ERR_PTR(-ENOENT); + } ++ } else if (result->d_op && result->d_op->d_revalidate_it) { ++ if (!result->d_op->d_revalidate_it(result, flags, it) && ++ !d_invalidate(result)) { ++ dput(result); ++ if (counter > 10) ++ result = ERR_PTR(-ESTALE); ++ if (!IS_ERR(result)) ++ goto again; ++ } + } + return result; + } +@@ -332,7 +364,8 @@ + * Without that kind of total limit, nasty chains of consecutive + * symlinks can cause almost arbitrarily long lookups. + */ +-static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd) ++static inline int do_follow_link(struct dentry *dentry, struct nameidata *nd, ++ struct lookup_intent *it) + { + int err; + if (current->link_count >= 5) +@@ -346,10 +379,12 @@ + current->link_count++; + current->total_link_count++; + UPDATE_ATIME(dentry->d_inode); ++ nd->intent = it; + err = dentry->d_inode->i_op->follow_link(dentry, nd); + current->link_count--; + return err; + loop: ++ intent_release(it); + path_release(nd); + return -ELOOP; + } +@@ -447,7 +482,8 @@ + * + * We expect 'base' to be positive and a directory. + */ +-int fastcall link_path_walk(const char * name, struct nameidata *nd) ++int fastcall link_path_walk_it(const char * name, struct nameidata *nd, ++ struct lookup_intent *it) + { + struct dentry *dentry; + struct inode *inode; +@@ -520,9 +556,9 @@ + break; + } + /* This does the actual lookups.. */ +- dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE); ++ dentry = cached_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL); + if (!dentry) { +- dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE); ++ dentry = real_lookup(nd->dentry, &this, LOOKUP_CONTINUE, NULL); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + break; +@@ -540,7 +576,7 @@ + goto out_dput; + + if (inode->i_op->follow_link) { +- err = do_follow_link(dentry, nd); ++ err = do_follow_link(dentry, nd, NULL); + dput(dentry); + if (err) + goto return_err; +@@ -556,7 +592,7 @@ + nd->dentry = dentry; + } + err = -ENOTDIR; +- if (!inode->i_op->lookup) ++ if (!inode->i_op->lookup && !inode->i_op->lookup_it) + break; + continue; + /* here ends the main loop */ +@@ -583,9 +619,9 @@ + if (err < 0) + break; + } +- dentry = cached_lookup(nd->dentry, &this, nd->flags); ++ dentry = cached_lookup(nd->dentry, &this, nd->flags, it); + if (!dentry) { +- dentry = real_lookup(nd->dentry, &this, nd->flags); ++ dentry = real_lookup(nd->dentry, &this, nd->flags, it); + err = PTR_ERR(dentry); + if (IS_ERR(dentry)) + break; +@@ -595,7 +631,7 @@ + inode = dentry->d_inode; + if ((lookup_flags & LOOKUP_FOLLOW) + && inode && inode->i_op && inode->i_op->follow_link) { +- err = do_follow_link(dentry, nd); ++ err = do_follow_link(dentry, nd, it); + dput(dentry); + if (err) + goto return_err; +@@ -609,7 +645,8 @@ + goto no_inode; + if (lookup_flags & LOOKUP_DIRECTORY) { + err = -ENOTDIR; +- if (!inode->i_op || !inode->i_op->lookup) ++ if (!inode->i_op || ++ (!inode->i_op->lookup && !inode->i_op->lookup_it)) + break; + } + goto return_base; +@@ -635,6 +672,34 @@ + * Check the cached dentry for staleness. + */ + dentry = nd->dentry; ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate_it) { ++ err = -ESTALE; ++ if (!dentry->d_op->d_revalidate_it(dentry, 0, it)) { ++ struct dentry *new; ++ err = permission(dentry->d_parent->d_inode, ++ MAY_EXEC); ++ if (err) ++ break; ++ new = real_lookup(dentry->d_parent, ++ &dentry->d_name, 0, it); ++ if (IS_ERR(new)) { ++ err = PTR_ERR(new); ++ break; ++ } ++ d_invalidate(dentry); ++ dput(dentry); ++ nd->dentry = new; ++ } ++ if (!nd->dentry->d_inode) ++ goto no_inode; ++ if (lookup_flags & LOOKUP_DIRECTORY) { ++ err = -ENOTDIR; ++ if (!nd->dentry->d_inode->i_op || ++ (!nd->dentry->d_inode->i_op->lookup && ++ !nd->dentry->d_inode->i_op->lookup_it)) ++ break; ++ } ++ } else + if (dentry && dentry->d_op && dentry->d_op->d_revalidate) { + err = -ESTALE; + if (!dentry->d_op->d_revalidate(dentry, 0)) { +@@ -648,15 +713,28 @@ + dput(dentry); + break; + } ++ if (err) ++ intent_release(it); + path_release(nd); + return_err: + return err; + } + ++int link_path_walk(const char * name, struct nameidata *nd) ++{ ++ return link_path_walk_it(name, nd, NULL); ++} ++ ++int path_walk_it(const char * name, struct nameidata *nd, struct lookup_intent *it) ++{ ++ current->total_link_count = 0; ++ return link_path_walk_it(name, nd, it); ++} ++ + int fastcall path_walk(const char * name, struct nameidata *nd) + { + current->total_link_count = 0; +- return link_path_walk(name, nd); ++ return link_path_walk_it(name, nd, NULL); + } + + /* SMP-safe */ +@@ -741,6 +819,16 @@ + } + + /* SMP-safe */ ++int path_lookup_it(const char *path, unsigned flags, struct nameidata *nd, ++ struct lookup_intent *it) ++{ ++ int error = 0; ++ if (path_init(path, flags, nd)) ++ error = path_walk_it(path, nd, it); ++ return error; ++} ++ ++/* SMP-safe */ + int fastcall path_lookup(const char *path, unsigned flags, struct nameidata *nd) + { + int error = 0; +@@ -755,6 +843,7 @@ + { + nd->last_type = LAST_ROOT; /* if there are only slashes... */ + nd->flags = flags; ++ nd->intent = NULL; + if (*name=='/') + return walk_init_root(name,nd); + read_lock(¤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); diff --git a/lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch b/lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch new file mode 100644 index 0000000..a0b643f --- /dev/null +++ b/lustre/kernel_patches/patches/vfs_races-2.6-fc3.patch @@ -0,0 +1,64 @@ +Index: linux-2.6.11/fs/dcache.c +=================================================================== +--- linux-2.6.11.orig/fs/dcache.c 2005-04-13 22:44:37.698760922 -0400 ++++ linux-2.6.11/fs/dcache.c 2005-04-13 22:44:50.482498026 -0400 +@@ -1183,7 +1183,7 @@ + spin_unlock(&dcache_lock); + } + +-static void __d_rehash(struct dentry * entry, struct hlist_head *list) ++static void __d_rehash_internal(struct dentry * entry, struct hlist_head *list) + { + + entry->d_flags &= ~DCACHE_UNHASHED; +@@ -1197,15 +1197,24 @@ + * Adds a dentry to the hash according to its name. + */ + +-void d_rehash(struct dentry * entry) ++void __d_rehash(struct dentry * entry, int lock) + { + struct hlist_head *list = d_hash(entry->d_parent, entry->d_name.hash); + +- spin_lock(&dcache_lock); ++ if (lock) ++ spin_lock(&dcache_lock); + spin_lock(&entry->d_lock); +- __d_rehash(entry, list); ++ __d_rehash_internal(entry, list); + spin_unlock(&entry->d_lock); +- spin_unlock(&dcache_lock); ++ if (lock) ++ spin_unlock(&dcache_lock); ++} ++ ++EXPORT_SYMBOL(__d_rehash); ++ ++void d_rehash(struct dentry * entry) ++{ ++ __d_rehash(entry, 1); + } + + #define do_switch(x,y) do { \ +@@ -1308,7 +1317,7 @@ + + already_unhashed: + list = d_hash(target->d_parent, target->d_name.hash); +- __d_rehash(dentry, list); ++ __d_rehash_internal(dentry, list); + + /* Unhash the target: dput() will then get rid of it */ + __d_drop(target); +Index: linux-2.6.11/include/linux/dcache.h +=================================================================== +--- linux-2.6.11.orig/include/linux/dcache.h 2005-04-13 22:35:23.515178600 -0400 ++++ linux-2.6.11/include/linux/dcache.h 2005-04-13 22:44:43.188221365 -0400 +@@ -159,6 +159,8 @@ + + #define DCACHE_REFERENCED 0x0008 /* Recently used, don't discard. */ + #define DCACHE_UNHASHED 0x0010 ++#define DCACHE_LUSTRE_INVALID 0x0020 /* Lustre invalidated */ ++ + + extern spinlock_t dcache_lock; + diff --git a/lustre/kernel_patches/series/2.6-fc3.series b/lustre/kernel_patches/series/2.6-fc3.series index 49c5c0e..4b6e21f 100644 --- a/lustre/kernel_patches/series/2.6-fc3.series +++ b/lustre/kernel_patches/series/2.6-fc3.series @@ -16,3 +16,6 @@ export-show_task-2.6-vanilla.patch sd_iostats-2.6-rhel4.patch jbd-2.6.10-jcberr.patch hostfs_readdir_large.patch +ext3-patch-fuzz-fixup-fc3.patch +uml-exprt-clearuser.patch +fsprivate-2.6.patch diff --git a/lustre/kernel_patches/series/2.6-rhel4.series b/lustre/kernel_patches/series/2.6-rhel4.series index 46487e1..e523bb3 100644 --- a/lustre/kernel_patches/series/2.6-rhel4.series +++ b/lustre/kernel_patches/series/2.6-rhel4.series @@ -14,3 +14,4 @@ lookup_bdev_init_intent.patch remove-suid-2.6-suse.patch export-show_task-2.6-vanilla.patch sd_iostats-2.6-rhel4.patch +fsprivate-2.6.patch diff --git a/lustre/kernel_patches/series/2.6-suse-lnxi.series b/lustre/kernel_patches/series/2.6-suse-lnxi.series index 59c9be2..86a2b59 100644 --- a/lustre/kernel_patches/series/2.6-suse-lnxi.series +++ b/lustre/kernel_patches/series/2.6-suse-lnxi.series @@ -9,3 +9,6 @@ export-filemap_populate.patch grab_cache_page_nowait_gfp-2.6-suse.patch remove-suid-2.6-suse.patch link_notlast-susefix.patch +uml-exprt-clearuser.patch +qsnet-suse-2.6.patch +fsprivate-2.6.patch diff --git a/lustre/kernel_patches/series/bgl-2.4.19 b/lustre/kernel_patches/series/bgl-2.4.19 index 0a03eda..bd67a30 100644 --- a/lustre/kernel_patches/series/bgl-2.4.19 +++ b/lustre/kernel_patches/series/bgl-2.4.19 @@ -40,6 +40,7 @@ jbd-2.4.19-pre1-jcberr.patch resched-2.4.19-pre1.patch ext3-xattr-ptr-arith-fix.patch vmalloc_to_page-2.4.19-bgl.patch +procfs-ndynamic-2.4.patch ext3-truncate-buffer-head.patch kallsyms-2.4-bgl.patch kksymoops-2.4-bgl.patch diff --git a/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series b/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series index 70e7b12..0980162 100644 --- a/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series +++ b/lustre/kernel_patches/series/ldiskfs-2.6-rhel4.series @@ -9,3 +9,4 @@ ext3-include-fixes-2.6-rhel4.patch ext3-extents-2.6.9-rhel4.patch ext3-mballoc2-2.6.9-rhel4.patch ext3-nlinks-2.6.7.patch +ext3-htree-dot-2.6.patch diff --git a/lustre/kernel_patches/series/ldiskfs-2.6-suse.series b/lustre/kernel_patches/series/ldiskfs-2.6-suse.series index fd05c25..c44eea3 100644 --- a/lustre/kernel_patches/series/ldiskfs-2.6-suse.series +++ b/lustre/kernel_patches/series/ldiskfs-2.6-suse.series @@ -10,3 +10,4 @@ ext3-extents-2.6.5.patch ext3-mballoc2-2.6-suse.patch ext3-nlinks-2.6.7.patch ext3-rename-reserve-2.6-suse.patch +ext3-htree-dot-2.6.5-suse.patch diff --git a/lustre/kernel_patches/series/rhel-2.4.21 b/lustre/kernel_patches/series/rhel-2.4.21 index 471eceb..2482df4 100644 --- a/lustre/kernel_patches/series/rhel-2.4.21 +++ b/lustre/kernel_patches/series/rhel-2.4.21 @@ -29,14 +29,13 @@ ext3-raw-lookup.patch nfs_export_kernel-2.4.21-chaos.patch ext3-ea-in-inode-2.4.21-chaos.patch listman-2.4.21-chaos.patch -ext3-xattr-ptr-arith-fix.patch pagecache-lock-2.4.21-chaos.patch ext3-truncate-buffer-head.patch inode-max-readahead-2.4.24.patch dcache_refcount_debug.patch ext3-extents-2.4.21-chaos.patch ext3-extents-asyncdel-2.4.21-chaos.patch -blkdev_tunables-2.4.21-chaos.patch +blkdev_tunables-2.4.21-chaos.patch small_scatterlist-2.4.21-rhel.patch ext3-nlinks-2.4.21-chaos.patch sd_iostats-2.4.21-chaos.patch @@ -46,3 +45,8 @@ export-show_task-2.4-rhel.patch compile-fixes-2.4.21-rhel.patch grab_cache_page_nowait_gfp-rh-2.4.patch remove-suid-2.4-rhel.patch +qsnet-rhel-2.4.patch +nfs_statfs-toomanyfiles-rhel-2.4.patch +statfs64-cast-unsigned-2.4-rhel.patch +fsprivate-2.4.patch +nfsd_iallocsem.patch diff --git a/lustre/kernel_patches/series/suse-2.4.21-cray b/lustre/kernel_patches/series/suse-2.4.21-cray index 20fab4c..c0187e4 100644 --- a/lustre/kernel_patches/series/suse-2.4.21-cray +++ b/lustre/kernel_patches/series/suse-2.4.21-cray @@ -30,5 +30,12 @@ ext3-xattr-ptr-arith-fix.patch procfs-ndynamic-2.4.21-suse2.patch ext3-truncate-buffer-head.patch loop-sync-2.4.21-suse.patch +inode-max-readahead-2.4.24.patch ext3-extents-2.4.21-suse2.patch ext3-extents-asyncdel-2.4.24.patch +ext3-nlinks-2.4.21-chaos.patch +export-show_task-2.4-cray.patch +grab_cache_page_nowait_gfp-2.4.21-suse2.patch +remove-suid-2.4-rhel.patch +fsprivate-2.4-suse.patch +nfsd_iallocsem.patch diff --git a/lustre/kernel_patches/series/suse-2.4.21-jvn b/lustre/kernel_patches/series/suse-2.4.21-jvn index b444822..ddcefe4 100644 --- a/lustre/kernel_patches/series/suse-2.4.21-jvn +++ b/lustre/kernel_patches/series/suse-2.4.21-jvn @@ -17,7 +17,6 @@ ext3-san-2.4.20.patch ext3-map_inode_page-2.4.21-suse2.patch ext3-error-export.patch iopen-2.4.19-suse.patch -tcp-zero-copy-2.4.21-suse-171.patch jbd-dont-account-blocks-twice.patch jbd-commit-tricks.patch ext3-no-write-super-chaos.patch @@ -28,3 +27,5 @@ ext3-ea-in-inode-2.4.21-sles.patch listman-2.4.20.patch ext3-truncate-buffer-head.patch lookup-stack-symbols-2.4.21-suse-171.patch +fsprivate-2.4-suse.patch +nfsd_iallocsem.patch diff --git a/lustre/kernel_patches/series/vanilla-2.4.24 b/lustre/kernel_patches/series/vanilla-2.4.24 index 9d16d81..a8ee3e0 100644 --- a/lustre/kernel_patches/series/vanilla-2.4.24 +++ b/lustre/kernel_patches/series/vanilla-2.4.24 @@ -43,3 +43,7 @@ export-show_task-2.4-vanilla.patch export-zap-page-range.patch uml-sigusr1-2.4-vanilla.patch remove-suid-2.4-rhel.patch +uml-exprt-clearuser.patch +fsprivate-2.4.patch +nfsd_iallocsem.patch +linux-2.4.24-jbd-handle-EIO.patch diff --git a/lustre/kernel_patches/series/vanilla-2.4.29 b/lustre/kernel_patches/series/vanilla-2.4.29 new file mode 100644 index 0000000..7b38830 --- /dev/null +++ b/lustre/kernel_patches/series/vanilla-2.4.29 @@ -0,0 +1,43 @@ +configurable-x86-stack-2.4.20.patch +configurable-x86_64-2.4.21.patch +dev_read_only_2.4.20-rh.patch +exports_2.4.20-rh-hp.patch +lustre_version.patch +vfs_intent-2.4.29-vanilla.patch +invalidate_show-2.4.29.patch +export-truncate.patch +iod-stock-exports-2.4.22.patch +ext3-htree-2.4.29.patch +linux-2.4.29-xattr-0.8.54.patch +ext3-orphan_lock-2.4.22-rh.patch +ext3-noread-2.4.20.patch +ext3-delete_thread-2.4.29.patch +extN-wantedi.patch +ext3-san-2.4.20.patch +ext3-map_inode_page.patch +ext3-error-export.patch +iopen-2.4.20.patch +tcp-zero-copy-2.4.22-rh.patch +jbd-dont-account-blocks-twice.patch +jbd-commit-tricks.patch +ext3-no-write-super-chaos.patch +add_page_private.patch +nfs_export_kernel-2.4.29.patch +ext3-raw-lookup.patch +ext3-ea-in-inode-2.4.29.patch +listman-2.4.20.patch +ext3-trusted_ea-2.4.20.patch +ext3-xattr-ptr-arith-fix.patch +3.5G-address-space-2.4.22-vanilla.patch +procfs-ndynamic-2.4.patch +ext3-truncate-buffer-head.patch +inode-max-readahead-2.4.24.patch +ext3-extents-2.4.29.patch +ext3-extents-asyncdel-2.4.24.patch +ext3-nlinks-2.4.24.patch +export-show_task-2.4-vanilla.patch +export-zap-page-range.patch +remove-suid-2.4-rhel.patch +kallsyms-2.4.29.patch +fsprivate-2.4.patch +nfsd_iallocsem.patch diff --git a/lustre/kernel_patches/series/vanilla-2.4.29-uml b/lustre/kernel_patches/series/vanilla-2.4.29-uml new file mode 100644 index 0000000..3bd5fd2 --- /dev/null +++ b/lustre/kernel_patches/series/vanilla-2.4.29-uml @@ -0,0 +1,47 @@ +uml-patch-2.4.29-1.patch +uml-2.4.20-do_mmap_pgoff-fix.patch +uml-export-end_iomem.patch +uml-exprt-clearuser.patch +configurable-x86-stack-2.4.20.patch +configurable-x86_64-2.4.21.patch +dev_read_only_2.4.20-rh.patch +exports_2.4.20-rh-hp.patch +lustre_version.patch +vfs_intent-2.4.29-vanilla.patch +invalidate_show-2.4.29.patch +export-truncate.patch +iod-stock-exports-2.4.22.patch +ext3-htree-2.4.29.patch +linux-2.4.29-xattr-0.8.54.patch +ext3-orphan_lock-2.4.22-rh.patch +ext3-noread-2.4.20.patch +ext3-delete_thread-2.4.29.patch +extN-wantedi.patch +ext3-san-2.4.20.patch +ext3-map_inode_page.patch +ext3-error-export.patch +iopen-2.4.20.patch +tcp-zero-copy-2.4.22-rh.patch +jbd-dont-account-blocks-twice.patch +jbd-commit-tricks.patch +ext3-no-write-super-chaos.patch +add_page_private.patch +nfs_export_kernel-2.4.29.patch +ext3-raw-lookup.patch +ext3-ea-in-inode-2.4.29.patch +listman-2.4.20.patch +ext3-trusted_ea-2.4.20.patch +ext3-xattr-ptr-arith-fix.patch +3.5G-address-space-2.4.22-vanilla.patch +procfs-ndynamic-2.4.patch +ext3-truncate-buffer-head.patch +inode-max-readahead-2.4.24.patch +ext3-extents-2.4.29.patch +ext3-extents-asyncdel-2.4.24.patch +ext3-nlinks-2.4.24.patch +export-show_task-2.4-vanilla.patch +export-zap-page-range.patch +remove-suid-2.4-rhel.patch +kallsyms-2.4.29.patch +fsprivate-2.4.patch +nfsd_iallocsem.patch diff --git a/lustre/kernel_patches/targets/2.6-rhel4.target.in b/lustre/kernel_patches/targets/2.6-rhel4.target.in index a44bc0f..de7cf84 100644 --- a/lustre/kernel_patches/targets/2.6-rhel4.target.in +++ b/lustre/kernel_patches/targets/2.6-rhel4.target.in @@ -1,5 +1,5 @@ lnxmaj="2.6.9" -lnxrel="5.0.3.EL" +lnxrel="5.0.5.EL" KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2 SERIES=2.6-rhel4.series @@ -7,6 +7,7 @@ VERSION=${lnxmaj} EXTRA_VERSION=${lnxrel}_lustre.@VERSION@ RHBUILD=1 LINUX26=1 +LUSTRE_VERSION=@VERSION@ BASE_ARCHS="i686 x86_64 ia64" BIGMEM_ARCHS="" diff --git a/lustre/kernel_patches/targets/2.6-suse.target.in b/lustre/kernel_patches/targets/2.6-suse.target.in index 4150cd1..a67deac 100644 --- a/lustre/kernel_patches/targets/2.6-suse.target.in +++ b/lustre/kernel_patches/targets/2.6-suse.target.in @@ -6,6 +6,7 @@ KERNEL=linux-$lnxmaj-$lnxrel.tar.bz2 SERIES=2.6-suse-lnxi.series VERSION=$lnxmaj EXTRA_VERSION="${lnxrel}_lustre.@VERSION@" +LUSTRE_VERSION=@VERSION@ RHBUILD=0 LINUX26=1 SUSEBUILD=1 diff --git a/lustre/kernel_patches/targets/2.6-vanilla.target.in b/lustre/kernel_patches/targets/2.6-vanilla.target.in index 00c05df..84bd491 100644 --- a/lustre/kernel_patches/targets/2.6-vanilla.target.in +++ b/lustre/kernel_patches/targets/2.6-vanilla.target.in @@ -4,6 +4,7 @@ KERNEL=linux-$lnxmaj.tar.gz SERIES=2.6-vanilla VERSION=$lnxmaj EXTRA_VERSION=lustre.@VERSION@ +LUSTRE_VERSION=@VERSION@ RHBUILD=0 BASE_ARCHS="" diff --git a/lustre/kernel_patches/targets/hp_pnnl-2.4.target.in b/lustre/kernel_patches/targets/hp_pnnl-2.4.target.in index 620e698..28c1be3 100644 --- a/lustre/kernel_patches/targets/hp_pnnl-2.4.target.in +++ b/lustre/kernel_patches/targets/hp_pnnl-2.4.target.in @@ -5,6 +5,7 @@ KERNEL=linux-$lnxmaj-$lnxrel.tar.gz SERIES=hp-pnnl-2.4.20 VERSION=$lnxmaj EXTRA_VERSION=$lnxrel_lustre.@VERSION@ +LUSTRE_VERSION=@VERSION@ RHBUILD=0 BASE_ARCHS="ia64" diff --git a/lustre/kernel_patches/targets/rh-2.4.target.in b/lustre/kernel_patches/targets/rh-2.4.target.in index fa9140d..d27ed40 100644 --- a/lustre/kernel_patches/targets/rh-2.4.target.in +++ b/lustre/kernel_patches/targets/rh-2.4.target.in @@ -5,6 +5,7 @@ KERNEL=linux-${lnxmaj}-${lnxrel}.tar.gz SERIES=rh-2.4.20 VERSION=$lnxmaj EXTRA_VERSION=${lnxrel}_lustre.@VERSION@ +LUSTRE_VERSION=@VERSION@ RHBUILD=1 BASE_ARCHS="i686" diff --git a/lustre/kernel_patches/targets/rhel-2.4.target.in b/lustre/kernel_patches/targets/rhel-2.4.target.in index b8ad58a..8982d8f 100644 --- a/lustre/kernel_patches/targets/rhel-2.4.target.in +++ b/lustre/kernel_patches/targets/rhel-2.4.target.in @@ -1,9 +1,10 @@ lnxmaj="2.4.21" -lnxrel="27.0.2.EL" +lnxrel="32.0.1.EL" KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2 SERIES=rhel-2.4.21 VERSION=${lnxmaj} +LUSTRE_VERSION=@VERSION@ EXTRA_VERSION=${lnxrel}_lustre.@VERSION@ RHBUILD=1 @@ -14,7 +15,7 @@ JENSEN_ARCHS="" SMP_ARCHS="i686 x86_64 ia64" UP_ARCHS="" -# the modules in this kernel do not build with gcc 3.3 or 2.96 +# the modules in this kernel do not build with gcc 4, 3.4, or 2.96 for cc in gcc32 gcc33 ; do if which $cc >/dev/null 2>/dev/null ; then export CC=$cc diff --git a/lustre/kernel_patches/targets/sles-2.4.target.in b/lustre/kernel_patches/targets/sles-2.4.target.in index b59fc56..784560a 100644 --- a/lustre/kernel_patches/targets/sles-2.4.target.in +++ b/lustre/kernel_patches/targets/sles-2.4.target.in @@ -5,6 +5,7 @@ KERNEL=linux-${lnxmaj}-${lnxrel}.tar.bz2 SERIES=suse-2.4.21-jvn VERSION=${lnxmaj} EXTRA_VERSION=${lnxrel}_lustre.@VERSION@ +LUSTRE_VERSION=@VERSION@ RHBUILD=0 LINUX26=0 SUSEBUILD=1 diff --git a/lustre/kernel_patches/targets/suse-2.4.21-2.target.in b/lustre/kernel_patches/targets/suse-2.4.21-2.target.in index 245c085..d00ca78 100644 --- a/lustre/kernel_patches/targets/suse-2.4.21-2.target.in +++ b/lustre/kernel_patches/targets/suse-2.4.21-2.target.in @@ -2,6 +2,7 @@ KERNEL=linux-2.4.21-x86_64.tar.gz SERIES=suse-2.4.21-2 VERSION=2.4.21 EXTRA_VERSION=lustre.@VERSION@ +LUSTRE_VERSION=@VERSION@ RHBUILD=0 BASE_ARCHS="x86_64" diff --git a/lustre/ldiskfs/Makefile.in b/lustre/ldiskfs/Makefile.in index be51da2..7236410 100644 --- a/lustre/ldiskfs/Makefile.in +++ b/lustre/ldiskfs/Makefile.in @@ -1,6 +1,6 @@ default: all -MODULES := ldiskfs quotafmt_test +MODULES := ldiskfs #quotafmt_test # copy makefile over to not break patches ext3_extra := $(wildcard @LINUX@/fs/ext3/Makefile) @@ -11,13 +11,13 @@ linux_headers := $(wildcard @LINUX@/include/linux/ext3*.h) ext3_sources := $(filter-out %.mod.c,$(wildcard @LINUX@/fs/ext3/*.c)) new_sources := iopen.c iopen.h extents.c mballoc.c new_headers := ext3_extents.h -quotafmt_sources := lustre_quota_fmt.c -quotafmt_headers := lustre_quota_fmt.h +#quotafmt_sources := lustre_quota_fmt.c +#quotafmt_headers := lustre_quota_fmt.h ldiskfs_patched_sources := $(notdir $(ext3_sources) $(ext3_headers)) $(new_sources) $(new_headers) -ldiskfs_sources := $(ldiskfs_patched_sources) $(quotafmt_sources) $(quotafmt_headers) +ldiskfs_sources := $(ldiskfs_patched_sources) #$(quotafmt_sources) $(quotafmt_headers) ldiskfs-objs := $(filter %.o,$(ldiskfs_sources:.c=.o)) -quotafmt-objs := quotafmt_test.o +#quotafmt-objs := quotafmt_test.o EXTRA_PRE_CFLAGS := -I@LINUX@/fs -I@LUSTRE@ -I@LUSTRE@/ldiskfs diff --git a/lustre/ldiskfs/autoMakefile.am b/lustre/ldiskfs/autoMakefile.am index 4f9e784..53ca41e 100644 --- a/lustre/ldiskfs/autoMakefile.am +++ b/lustre/ldiskfs/autoMakefile.am @@ -41,10 +41,12 @@ if USE_QUILT cd linux-stage && quilt setup -l ../$(series) -d ../$(patches) cd linux-stage && quilt push -a -q else + @echo -n "Applying ext3 patches:" @cd linux-stage && for i in $$(<../$(series)) ; do \ - echo "patch -p1 < ../$(patches)/$$i" ; \ - patch -p1 < ../$(patches)/$$i || exit 1 ; \ + echo -n " $$i" ; \ + patch -s -p1 < ../$(patches)/$$i || exit 1 ; \ done + @echo endif mkdir linux @echo -n "Replacing 'ext3' with 'ldiskfs':" @@ -70,7 +72,7 @@ foo-check: @echo "ldiskfs_OBJECTS: $(ldiskfs_OBJECTS)" @echo "ldiskfs_LDADD: $(ldiskfs_LDADD)" -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ CLEANFILES = sources $(notdir $(linux_headers) $(ext3_headers) $(ext3_sources) $(new_sources) $(new_headers)) EXTRA_DIST := lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c diff --git a/lustre/ldiskfs/lustre_quota_fmt.c b/lustre/ldiskfs/lustre_quota_fmt.c index 84aa304..cf2a23f 100644 --- a/lustre/ldiskfs/lustre_quota_fmt.c +++ b/lustre/ldiskfs/lustre_quota_fmt.c @@ -1,4 +1,6 @@ -/* +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * * Lustre administrative quota format. * * from @@ -40,7 +42,7 @@ int lustre_check_quota_file(struct lustre_quota_info *lqi, int type) loff_t offset = 0; static const uint quota_magics[] = LUSTRE_INITQMAGICS; static const uint quota_versions[] = LUSTRE_INITQVERSIONS; - + fs = get_fs(); set_fs(KERNEL_DS); size = f->f_op->read(f, (char *)&dqhead, sizeof(struct lustre_disk_dqheader), &offset); @@ -212,10 +214,10 @@ out_buf: } /* Insert empty block to the list */ -static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, +static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, dqbuf_t buf, uint blk) { - struct lustre_disk_dqdbheader *dh = (struct lustre_disk_dqdbheader *)buf; + struct lustre_disk_dqdbheader *dh =(struct lustre_disk_dqdbheader *)buf; int err; dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); @@ -223,7 +225,8 @@ static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, dh->dqdh_entries = cpu_to_le16(0); info->dqi_free_blk = blk; lustre_mark_info_dirty(info); - if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */ + if ((err = write_blk(filp, blk, buf)) < 0) + /* Some strange block. We had better leave it... */ return err; return 0; } @@ -536,7 +539,8 @@ static int remove_tree(struct lustre_dquot *dquot, uint *blk, int depth) int i; ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ - if (i == LUSTRE_DQBLKSIZE) { + /* don't put the root block into free blk list! */ + if (i == LUSTRE_DQBLKSIZE && *blk != LUSTRE_DQTREEOFF) { put_free_dqblk(filp, info, buf, *blk); *blk = 0; } @@ -550,6 +554,7 @@ out_buf: } /* Delete dquot from tree */ +#ifndef QFMT_NO_DELETE static int lustre_delete_dquot(struct lustre_dquot *dquot) { uint tmp = LUSTRE_DQTREEOFF; @@ -558,6 +563,7 @@ static int lustre_delete_dquot(struct lustre_dquot *dquot) return 0; return remove_tree(dquot, &tmp, 0); } +#endif /* Find entry in block */ static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk) @@ -669,7 +675,8 @@ int lustre_read_dquot(struct lustre_dquot *dquot) /* We need to escape back all-zero structure */ memset(&empty, 0, sizeof(struct lustre_disk_dqblk)); empty.dqb_itime = cpu_to_le64(1); - if (!memcmp(&empty, &ddquot, sizeof(struct lustre_disk_dqblk))) + if (!memcmp(&empty, &ddquot, + sizeof(struct lustre_disk_dqblk))) ddquot.dqb_itime = 0; } set_fs(fs); @@ -683,24 +690,19 @@ int lustre_read_dquot(struct lustre_dquot *dquot) int lustre_commit_dquot(struct lustre_dquot *dquot) { int rc = 0; - /* We clear the flag everytime so we don't loop when there was an IO error... */ + /* always clear the flag so we don't loop on an IO error... */ clear_bit(DQ_MOD_B, &dquot->dq_flags); - /* The block/inode usage in admin quotafile isn't the real usage over all cluster, - * so keep the fake dquot entry on disk is meaningless, just remove it */ -#ifndef QFMT_NO_DELETE + /* The block/inode usage in admin quotafile isn't the real usage + * over all cluster, so keep the fake dquot entry on disk is + * meaningless, just remove it */ if (test_bit(DQ_FAKE_B, &dquot->dq_flags)) rc = lustre_delete_dquot(dquot); - } - else { + else rc = lustre_write_dquot(dquot); - } -#else - rc = lustre_write_dquot(dquot); -#endif if (rc < 0) return rc; - + if (lustre_info_dirty(&dquot->dq_info->qi_info[dquot->dq_type])) rc = lustre_write_quota_info(dquot->dq_info, dquot->dq_type); @@ -723,7 +725,7 @@ int lustre_init_quota_info(struct lustre_quota_info *lqi, int type) /* write quotafile header */ dqhead.dqh_magic = cpu_to_le32(quota_magics[type]); dqhead.dqh_version = cpu_to_le32(quota_versions[type]); - size = fp->f_op->write(fp, (char *)&dqhead, + size = fp->f_op->write(fp, (char *)&dqhead, sizeof(struct lustre_disk_dqheader), &offset); if (size != sizeof(struct lustre_disk_dqheader)) { diff --git a/lustre/ldiskfs/quotafmt_test.c b/lustre/ldiskfs/quotafmt_test.c index 2c76acb..53985d2 100644 --- a/lustre/ldiskfs/quotafmt_test.c +++ b/lustre/ldiskfs/quotafmt_test.c @@ -1,4 +1,9 @@ -/* Kernel module to test lustre administrative quotafile format APIs +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * No redistribution or use is permitted outside of Cluster File Systems, Inc. + * + * Kernel module to test lustre administrative quotafile format APIs * from the OBD setup function */ #ifndef EXPORT_SYMTAB # define EXPORT_SYMTAB @@ -18,20 +23,21 @@ char *test_quotafile[2] = {"usrquota_test", "grpquota_test"}; -static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device *tgt, - struct obd_run_ctxt *saved) +static int quotfmt_initialize(struct lustre_quota_info *lqi, + struct obd_device *tgt, + struct lvfs_run_ctxt *saved) { struct lustre_disk_dqheader dqhead; static const uint quota_magics[] = LUSTRE_INITQMAGICS; static const uint quota_versions[] = LUSTRE_INITQVERSIONS; struct file *fp; - struct inode *parent_inode = tgt->obd_ctxt.pwd->d_inode; + struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode; size_t size; struct dentry *de; int i, rc = 0; ENTRY; - - push_ctxt(saved, &tgt->obd_ctxt, NULL); + + push_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL); sema_init(&lqi->qi_sem, 1); @@ -42,13 +48,13 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device * /* remove the stale test quotafile */ down(&parent_inode->i_sem); - de = lookup_one_len(name, tgt->obd_ctxt.pwd, namelen); + de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen); if (!IS_ERR(de) && de->d_inode) vfs_unlink(parent_inode, de); if (!IS_ERR(de)) dput(de); up(&parent_inode->i_sem); - + /* create quota file */ fp = filp_open(name, O_CREAT | O_EXCL, 0644); if (IS_ERR(fp)) { @@ -58,15 +64,15 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device * break; } lqi->qi_files[i] = fp; - + /* write quotafile header */ dqhead.dqh_magic = cpu_to_le32(quota_magics[i]); dqhead.dqh_version = cpu_to_le32(quota_versions[i]); - size = fp->f_op->write(fp, (char *)&dqhead, - sizeof(struct lustre_disk_dqheader), + size = fp->f_op->write(fp, (char *)&dqhead, + sizeof(struct lustre_disk_dqheader), &offset); if (size != sizeof(struct lustre_disk_dqheader)) { - CERROR("error writing quoafile header %s (rc = %d)\n", + CERROR("error writing quoafile header %s (rc = %d)\n", name, rc); rc = size; break; @@ -76,11 +82,12 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, struct obd_device * RETURN(rc); } -static int quotfmt_finalize(struct lustre_quota_info *lqi, struct obd_device *tgt, - struct obd_run_ctxt *saved) +static int quotfmt_finalize(struct lustre_quota_info *lqi, + struct obd_device *tgt, + struct lvfs_run_ctxt *saved) { struct dentry *de; - struct inode *parent_inode = tgt->obd_ctxt.pwd->d_inode; + struct inode *parent_inode = tgt->obd_lvfs_ctxt.pwd->d_inode; int i, rc = 0; ENTRY; @@ -90,21 +97,21 @@ static int quotfmt_finalize(struct lustre_quota_info *lqi, struct obd_device *tg if (lqi->qi_files[i] == NULL) continue; - + /* close quota file */ filp_close(lqi->qi_files[i], 0); - + /* unlink quota file */ down(&parent_inode->i_sem); - - de = lookup_one_len(name, tgt->obd_ctxt.pwd, namelen); + + de = lookup_one_len(name, tgt->obd_lvfs_ctxt.pwd, namelen); if (IS_ERR(de) || de->d_inode == NULL) { rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT; - CERROR("error lookup quotafile %s (rc = %d)\n", + CERROR("error lookup quotafile %s (rc = %d)\n", name, rc); goto dput; } - + rc = vfs_unlink(parent_inode, de); if (rc) CERROR("error unlink quotafile %s (rc = %d)\n", @@ -115,7 +122,7 @@ dput: up(&parent_inode->i_sem); } - pop_ctxt(saved, &tgt->obd_ctxt, NULL); + pop_ctxt(saved, &tgt->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -123,7 +130,7 @@ static int quotfmt_test_1(struct lustre_quota_info *lqi) { int i; ENTRY; - + for (i = 0; i < MAXQUOTAS; i++) { if (!lustre_check_quota_file(lqi, i)) RETURN(-EINVAL); @@ -141,9 +148,9 @@ static void print_quota_info(struct lustre_quota_info *lqi) dqinfo = &lqi->qi_info[i]; printk("%s quota info:\n", i == USRQUOTA ? "user " : "group"); printk("dqi_bgrace(%u) dqi_igrace(%u) dqi_flags(%lu) dqi_blocks(%u) " - "dqi_free_blk(%u) dqi_free_entry(%u)\n", - dqinfo->dqi_bgrace, dqinfo->dqi_igrace, dqinfo->dqi_flags, - dqinfo->dqi_blocks, dqinfo->dqi_free_blk, + "dqi_free_blk(%u) dqi_free_entry(%u)\n", + dqinfo->dqi_bgrace, dqinfo->dqi_igrace, dqinfo->dqi_flags, + dqinfo->dqi_blocks, dqinfo->dqi_free_blk, dqinfo->dqi_free_entry); } #endif @@ -156,7 +163,7 @@ static int quotfmt_test_2(struct lustre_quota_info *lqi) for (i = 0; i < MAXQUOTAS; i++) { struct lustre_mem_dqinfo dqinfo; - + rc = lustre_init_quota_info(lqi, i); if (rc) { CERROR("init quotainfo(%d) failed! (rc:%d)\n", i, rc); @@ -169,7 +176,7 @@ static int quotfmt_test_2(struct lustre_quota_info *lqi) CERROR("read quotainfo(%d) failed! (rc:%d)\n", i, rc); break; } - + if(memcmp(&dqinfo, &lqi->qi_info[i], sizeof(dqinfo))) { rc = -EINVAL; break; @@ -203,7 +210,7 @@ static struct lustre_dquot *get_rand_dquot(struct lustre_quota_info *lqi) dquot->dq_dqb.dqb_curinodes = rand / 3; dquot->dq_dqb.dqb_btime = jiffies; dquot->dq_dqb.dqb_itime = jiffies; - + return dquot; } @@ -218,7 +225,7 @@ static int write_check_dquot(struct lustre_quota_info *lqi) struct mem_dqblk dqblk; int rc = 0; ENTRY; - + dquot = get_rand_dquot(lqi); if (dquot == NULL) RETURN(-ENOMEM); @@ -260,14 +267,11 @@ static int quotfmt_test_3(struct lustre_quota_info *lqi) int i = 0, rc = 0; ENTRY; -#ifdef QFMT_NO_DELETE - RETURN(0); -#endif dquot = get_rand_dquot(lqi); if (dquot == NULL) RETURN(-ENOMEM); repeat: - clear_bit(DQ_FAKE_B, dquot->dq_flags); + clear_bit(DQ_FAKE_B, &dquot->dq_flags); /* write a new dquot */ rc = lustre_commit_dquot(dquot); if (rc) { @@ -283,11 +287,11 @@ repeat: CERROR("read dquot failed! (rc:%d)\n", rc); GOTO(out, rc); } - if (!dquot->dq_off || test_bit(DQ_FAKE_B, dquot->dq_flags)) { + if (!dquot->dq_off || test_bit(DQ_FAKE_B, &dquot->dq_flags)) { CERROR("the dquot isn't committed\n"); GOTO(out, rc = -EINVAL); } - + /* remove this dquot */ set_bit(DQ_FAKE_B, &dquot->dq_flags); dquot->dq_dqb.dqb_curspace = 0; @@ -314,9 +318,9 @@ repeat: /* check if this dquot can be write again */ if (++i < 2) goto repeat; - - print_quota_info(lqi); - + + print_quota_info(lqi); + out: put_rand_dquot(dquot); RETURN(rc); @@ -330,7 +334,7 @@ static int quotfmt_test_4(struct lustre_quota_info *lqi) for (i = 0; i < 30000; i++) { rc = write_check_dquot(lqi); if (rc) { - CERROR("write/check dquot failed at %d! (rc:%d)\n", + CERROR("write/check dquot failed at %d! (rc:%d)\n", i, rc); break; } @@ -341,17 +345,17 @@ static int quotfmt_test_4(struct lustre_quota_info *lqi) static int quotfmt_run_tests(struct obd_device *obd, struct obd_device *tgt) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct lustre_quota_info *lqi = NULL; int rc = 0; ENTRY; - + OBD_ALLOC(lqi, sizeof(*lqi)); if (lqi == NULL) { CERROR("not enough memory\n"); RETURN(-ENOMEM); } - + CWARN("=== Initialize quotafile test\n"); rc = quotfmt_initialize(lqi, tgt, &saved); if (rc) @@ -434,16 +438,18 @@ static struct obd_ops quotfmt_obd_ops = { .o_cleanup = quotfmt_test_cleanup, }; +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; LPROCFS_INIT_VARS(quotfmt_test, lprocfs_module_vars, lprocfs_obd_vars) +#endif static int __init quotfmt_test_init(void) { struct lprocfs_static_vars lvars; lprocfs_init_vars(quotfmt_test, &lvars); - return class_register_type("fmt_obd_ops, lvars.module_vars, + return class_register_type("fmt_obd_ops, lvars.module_vars, "quotfmt_test"); } diff --git a/lustre/ldlm/Makefile.am b/lustre/ldlm/Makefile.am index 5c3cc01..2b9856c 100644 --- a/lustre/ldlm/Makefile.am +++ b/lustre/ldlm/Makefile.am @@ -7,7 +7,7 @@ # ldlm is built into ptlrpc # -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ DIST_SOURCES = ldlm_extent.c ldlm_flock.c ldlm_internal.h ldlm_lib.c \ ldlm_lock.c ldlm_lockd.c ldlm_plain.c ldlm_request.c \ ldlm_resource.c l_lock.c diff --git a/lustre/ldlm/ldlm_extent.c b/lustre/ldlm/ldlm_extent.c index 1d87959..0cd1240 100644 --- a/lustre/ldlm/ldlm_extent.c +++ b/lustre/ldlm/ldlm_extent.c @@ -147,10 +147,15 @@ static void ldlm_extent_policy(struct ldlm_resource *res, /* Determine if the lock is compatible with all locks on the queue. * We stop walking the queue if we hit ourselves so we don't take - * conflicting locks enqueued after us into accound, or we'd wait forever. */ + * conflicting locks enqueued after us into accound, or we'd wait forever. + * 0 if the lock is not compatible + * 1 if the lock is compatible + * 2 if this group lock is compatible and requires no further checking + * negative error, such as EWOULDBLOCK for group locks + */ static int ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req, - int send_cbs) + int send_cbs, int *flags, ldlm_error_t *err) { struct list_head *tmp; struct ldlm_lock *lock; @@ -158,6 +163,7 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req, __u64 req_start = req->l_req_extent.start; __u64 req_end = req->l_req_extent.end; int compat = 1; + int scan = 0; ENTRY; lockmode_verify(req_mode); @@ -168,14 +174,111 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req, if (req == lock) RETURN(compat); + if (unlikely(scan)) { + /* We only get here if we are queuing GROUP lock + and met some incompatible one. The main idea of this + code is to insert GROUP lock past compatible GROUP + lock in the waiting queue or if there is not any, + then in front of first non-GROUP lock */ + if (lock->l_req_mode != LCK_GROUP) { + /* Ok, we hit non-GROUP lock, there should be no + more GROUP locks later on, queue in front of + first non-GROUP lock */ + + ldlm_resource_insert_lock_after(lock, req); + list_del_init(&lock->l_res_link); + ldlm_resource_insert_lock_after(req, lock); + RETURN(0); + } + if (req->l_policy_data.l_extent.gid == + lock->l_policy_data.l_extent.gid) { + /* found it */ + ldlm_resource_insert_lock_after(lock, + req); + RETURN(0); + } + continue; + } + /* locks are compatible, overlap doesn't matter */ - if (lockmode_compat(lock->l_req_mode, req_mode)) + if (lockmode_compat(lock->l_req_mode, req_mode)) { + /* non-group locks are compatible, overlap doesn't + matter */ + if (likely(req_mode != LCK_GROUP)) + continue; + + /* If we are trying to get a GROUP lock and there is + another one of this kind, we need to compare gid */ + if (req->l_policy_data.l_extent.gid == + lock->l_policy_data.l_extent.gid) { + /* If existing lock with matched gid is granted, + we grant new one too. */ + if (lock->l_req_mode == lock->l_granted_mode) + RETURN(2); + + /* Otherwise we are scanning queue of waiting + * locks and it means current request would + * block along with existing lock (that is + * already blocked. + * If we are in nonblocking mode - return + * immediately */ + if (*flags & LDLM_FL_BLOCK_NOWAIT) { + compat = -EWOULDBLOCK; + goto destroylock; + } + /* If this group lock is compatible with another + * group lock on the waiting list, they must be + * together in the list, so they can be granted + * at the same time. Otherwise the later lock + * can get stuck behind another, incompatible, + * lock. */ + ldlm_resource_insert_lock_after(lock, req); + /* Because 'lock' is not granted, we can stop + * processing this queue and return immediately. + * There is no need to check the rest of the + * list. */ + RETURN(0); + } + } + + if (unlikely(req_mode == LCK_GROUP && + (lock->l_req_mode != lock->l_granted_mode))) { + scan = 1; + compat = 0; + if (lock->l_req_mode != LCK_GROUP) { + /* Ok, we hit non-GROUP lock, there should be no + more GROUP locks later on, queue in front of + first non-GROUP lock */ + + ldlm_resource_insert_lock_after(lock, req); + list_del_init(&lock->l_res_link); + ldlm_resource_insert_lock_after(req, lock); + RETURN(0); + } + if (req->l_policy_data.l_extent.gid == + lock->l_policy_data.l_extent.gid) { + /* found it */ + ldlm_resource_insert_lock_after(lock, req); + RETURN(0); + } continue; + } - /* if lock doesn't overlap skip it */ - if (lock->l_policy_data.l_extent.end < req_start || - lock->l_policy_data.l_extent.start > req_end) + if (unlikely(lock->l_req_mode == LCK_GROUP)) { + /* If compared lock is GROUP,then requested is PR/PW/ => + * this is not compatible; extent range does not + * matter */ + if (*flags & LDLM_FL_BLOCK_NOWAIT) { + compat = -EWOULDBLOCK; + goto destroylock; + } else { + *flags |= LDLM_FL_NO_TIMEOUT; + } + } else if (lock->l_policy_data.l_extent.end < req_start || + lock->l_policy_data.l_extent.start > req_end) { + /* if a non group lock doesn't overlap skip it */ continue; + } if (!send_cbs) RETURN(0); @@ -185,6 +288,11 @@ ldlm_extent_compat_queue(struct list_head *queue, struct ldlm_lock *req, ldlm_add_ast_work_item(lock, req, NULL, 0); } + return(compat); +destroylock: + list_del_init(&req->l_res_link); + ldlm_lock_destroy(req); + *err = compat; RETURN(compat); } @@ -202,18 +310,21 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq, { struct ldlm_resource *res = lock->l_resource; struct list_head rpc_list = LIST_HEAD_INIT(rpc_list); - int rc; + int rc, rc2; ENTRY; LASSERT(list_empty(&res->lr_converting)); + *err = ELDLM_OK; if (!first_enq) { LASSERT(res->lr_tmp != NULL); - rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 0); - if (!rc) - RETURN(LDLM_ITER_STOP); - rc = ldlm_extent_compat_queue(&res->lr_waiting, lock, 0); - if (!rc) + rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 0, flags, + err); + if (rc == 1) { + rc = ldlm_extent_compat_queue(&res->lr_waiting, lock, 0, + flags, err); + } + if (rc == 0) RETURN(LDLM_ITER_STOP); ldlm_resource_unlink_lock(lock); @@ -226,12 +337,26 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq, restart: LASSERT(res->lr_tmp == NULL); res->lr_tmp = &rpc_list; - rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 1); - rc += ldlm_extent_compat_queue(&res->lr_waiting, lock, 1); + rc = ldlm_extent_compat_queue(&res->lr_granted, lock, 1, flags, err); + if (rc < 0) + GOTO(out, rc); /* lock was destroyed */ + if (rc == 2) { + res->lr_tmp = NULL; + goto grant; + } + + rc2 = ldlm_extent_compat_queue(&res->lr_waiting, lock, 1, flags, err); + if (rc2 < 0) + GOTO(out, rc = rc2); /* lock was destroyed */ res->lr_tmp = NULL; - if (rc != 2) { - /* If either of the compat_queue()s returned 0, then we + if (rc + rc2 == 2) { + grant: + ldlm_extent_policy(res, lock, flags); + ldlm_resource_unlink_lock(lock); + ldlm_grant_lock(lock, NULL, 0, 0); + } else { + /* If either of the compat_queue()s returned failure, then we * have ASTs to send and must go onto the waiting list. * * bug 2322: we used to unlink and re-add here, which was a @@ -245,12 +370,11 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq, if (rc == -ERESTART) GOTO(restart, -ERESTART); *flags |= LDLM_FL_BLOCK_GRANTED; - } else { - ldlm_extent_policy(res, lock, flags); - ldlm_resource_unlink_lock(lock); - ldlm_grant_lock(lock, NULL, 0, 0); } - RETURN(0); + rc = 0; +out: + res->lr_tmp = NULL; + RETURN(rc); } /* When a lock is cancelled by a client, the KMS may undergo change if this diff --git a/lustre/ldlm/ldlm_flock.c b/lustre/ldlm/ldlm_flock.c index f4d660a..b4bae36 100644 --- a/lustre/ldlm/ldlm_flock.c +++ b/lustre/ldlm/ldlm_flock.c @@ -31,6 +31,7 @@ #include <libcfs/list.h> #else #include <liblustre.h> +#include <linux/obd_class.h> #endif #include "ldlm_internal.h" @@ -362,9 +363,11 @@ ldlm_process_flock_lock(struct ldlm_lock *req, int *flags, int first_enq, break; } + /* At this point we're granting the lock request. */ + req->l_granted_mode = req->l_req_mode; + /* Add req to the granted queue before calling ldlm_reprocess_all(). */ if (!added) { - req->l_granted_mode = req->l_req_mode; list_del_init(&req->l_res_link); /* insert new lock before ownlocks in list. */ ldlm_resource_add_lock(res, ownlocks, req); @@ -431,6 +434,9 @@ ldlm_flock_interrupted_wait(void *data) /* take lock off the deadlock detection waitq. */ list_del_init(&lock->l_flock_waitq); + /* client side - set flag to prevent lock from being put on lru list */ + lock->l_flags |= LDLM_FL_CBPENDING; + ldlm_lock_decref_internal(lock, lock->l_req_mode); ldlm_lock2handle(lock, &lockh); ldlm_cli_cancel(&lockh); @@ -456,11 +462,6 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data) LASSERT(flags != LDLM_FL_WAIT_NOREPROC); - if (flags == 0) { - wake_up(&lock->l_waitq); - RETURN(0); - } - if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED | LDLM_FL_BLOCK_CONV))) goto granted; @@ -468,8 +469,6 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data) LDLM_DEBUG(lock, "client-side enqueue returned a blocked lock, " "sleeping"); - ldlm_lock_dump(D_OTHER, lock, 0); - fwd.fwd_lock = lock; obd = class_exp2obd(lock->l_conn_export); @@ -490,17 +489,12 @@ ldlm_flock_completion_ast(struct ldlm_lock *lock, int flags, void *data) ((lock->l_req_mode == lock->l_granted_mode) || lock->l_destroyed), &lwi); - if (rc) { - LDLM_DEBUG(lock, "client-side enqueue waking up: failed (%d)", - rc); - RETURN(rc); - } - - LASSERT(!(lock->l_destroyed)); - + LDLM_DEBUG(lock, "client-side enqueue waking up: rc = %d", rc); + RETURN(rc); + granted: - LDLM_DEBUG(lock, "client-side enqueue waking up"); + LDLM_DEBUG(lock, "client-side enqueue granted"); ns = lock->l_resource->lr_namespace; l_lock(&ns->ns_lock); @@ -529,14 +523,18 @@ granted: getlk->fl_start = lock->l_policy_data.l_flock.start; getlk->fl_end = lock->l_policy_data.l_flock.end; } else { + int noreproc = LDLM_FL_WAIT_NOREPROC; + /* We need to reprocess the lock to do merges or splits * with existing locks owned by this process. */ - flags = LDLM_FL_WAIT_NOREPROC; - ldlm_process_flock_lock(lock, &flags, 1, &err); + ldlm_process_flock_lock(lock, &noreproc, 1, &err); + if (flags == 0) + wake_up(&lock->l_waitq); } l_unlock(&ns->ns_lock); RETURN(0); } +EXPORT_SYMBOL(ldlm_flock_completion_ast); int ldlm_flock_blocking_ast(struct ldlm_lock *lock, struct ldlm_lock_desc *desc, void *data, int flag) diff --git a/lustre/ldlm/ldlm_internal.h b/lustre/ldlm/ldlm_internal.h index ccaafdc..2967ab8 100644 --- a/lustre/ldlm/ldlm_internal.h +++ b/lustre/ldlm/ldlm_internal.h @@ -10,6 +10,10 @@ typedef enum { int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync); +/* ldlm_resource.c */ +void ldlm_resource_insert_lock_after(struct ldlm_lock *original, + struct ldlm_lock *new); + /* ldlm_lock.c */ void ldlm_grant_lock(struct ldlm_lock *lock, void *data, int datalen, int run_ast); diff --git a/lustre/ldlm/ldlm_lib.c b/lustre/ldlm/ldlm_lib.c index 4a1fe80..7338654 100644 --- a/lustre/ldlm/ldlm_lib.c +++ b/lustre/ldlm/ldlm_lib.c @@ -71,7 +71,6 @@ static int import_set_conn(struct obd_import *imp, struct obd_uuid *uuid, if (priority) { list_del(&item->oic_item); list_add(&item->oic_item, &imp->imp_conn_list); - item->oic_last_attempt = 0; } CDEBUG(D_HA, "imp %p@%s: found existing conn %s%s\n", imp, imp->imp_obd->obd_name, uuid->uuid, @@ -84,7 +83,6 @@ static int import_set_conn(struct obd_import *imp, struct obd_uuid *uuid, if (create) { imp_conn->oic_conn = ptlrpc_conn; imp_conn->oic_uuid = *uuid; - imp_conn->oic_last_attempt = 0; if (priority) list_add(&imp_conn->oic_item, &imp->imp_conn_list); else @@ -122,13 +120,13 @@ int client_import_add_conn(struct obd_import *imp, struct obd_uuid *uuid, int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid) { struct obd_import_conn *imp_conn; + struct obd_import_conn *cur_conn; struct obd_export *dlmexp; int rc = -ENOENT; ENTRY; spin_lock(&imp->imp_lock); if (list_empty(&imp->imp_conn_list)) { - LASSERT(!imp->imp_conn_current); LASSERT(!imp->imp_connection); GOTO(out, rc); } @@ -138,8 +136,12 @@ int client_import_del_conn(struct obd_import *imp, struct obd_uuid *uuid) continue; LASSERT(imp_conn->oic_conn); + cur_conn = list_entry(imp->imp_conn_list.next, + struct obd_import_conn, + oic_item); + /* is current conn? */ - if (imp_conn == imp->imp_conn_current) { + if (imp_conn == cur_conn) { LASSERT(imp_conn->oic_conn == imp->imp_connection); if (imp->imp_state != LUSTRE_IMP_CLOSED && @@ -545,7 +547,7 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler) LASSERT_REQSWAB (req, 0); str = lustre_msg_string(req->rq_reqmsg, 0, sizeof(tgtuuid) - 1); if (str == NULL) { - CERROR("bad target UUID for connect\n"); + DEBUG_REQ(D_ERROR, req, "bad target UUID for connect\n"); GOTO(out, rc = -EINVAL); } @@ -556,14 +558,17 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler) } if (!target || target->obd_stopping || !target->obd_set_up) { - CERROR("UUID '%s' is not available for connect\n", str); + DEBUG_REQ(D_ERROR, req, "UUID '%s' is not available " + " for connect (%s)\n", str, + !target ? "no target" : + (target->obd_stopping ? "stopping" : "not set up")); GOTO(out, rc = -ENODEV); } LASSERT_REQSWAB (req, 1); str = lustre_msg_string(req->rq_reqmsg, 1, sizeof(cluuid) - 1); if (str == NULL) { - CERROR("bad client UUID for connect\n"); + DEBUG_REQ(D_ERROR, req, "bad client UUID for connect\n"); GOTO(out, rc = -EINVAL); } @@ -597,9 +602,6 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler) memcpy(&conn, tmp, sizeof conn); data = lustre_swab_reqbuf(req, 3, sizeof(*data), lustre_swab_connect); - if (data == NULL) - GOTO(out, rc = -EPROTO); - rc = lustre_pack_reply(req, 1, &size, NULL); if (rc) GOTO(out, rc); @@ -642,8 +644,9 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler) if (export == NULL) { if (target->obd_recovering) { - CERROR("denying connection for new client %s: " - "%d clients in recovery for %lds\n", cluuid.uuid, + CERROR("%s: denying connection for new client %s: " + "%d clients in recovery for %lds\n", + target->obd_name, cluuid.uuid, target->obd_recoverable_clients, (target->obd_recovery_timer.expires-jiffies)/HZ); rc = -EBUSY; @@ -655,8 +658,9 @@ int target_handle_connect(struct ptlrpc_request *req, svc_handler_t handler) /* Return only the parts of obd_connect_data that we understand, so the * client knows that we don't understand the rest. */ - memcpy(lustre_msg_buf(req->rq_repmsg, 0, sizeof(*data)), data, - sizeof(*data)); + if (data) + memcpy(lustre_msg_buf(req->rq_repmsg, 0, sizeof(*data)), data, + sizeof(*data)); /* If all else goes well, this is our RPC return code. */ req->rq_status = 0; @@ -844,6 +848,7 @@ void target_cleanup_recovery(struct obd_device *obd) { struct list_head *tmp, *n; struct ptlrpc_request *req; + ENTRY; LASSERT(obd->obd_stopping); @@ -868,6 +873,7 @@ void target_cleanup_recovery(struct obd_device *obd) list_del(&req->rq_list); target_release_saved_req(req); } + EXIT; } void target_abort_recovery(void *data) @@ -881,6 +887,7 @@ void target_abort_recovery(void *data) return; } obd->obd_recovering = obd->obd_abort_recovery = 0; + obd->obd_recoverable_clients = 0; target_cancel_recovery_timer(obd); spin_unlock_bh(&obd->obd_processing_task_lock); @@ -897,7 +904,7 @@ void target_abort_recovery(void *data) static void target_recovery_expired(unsigned long castmeharder) { struct obd_device *obd = (struct obd_device *)castmeharder; - CERROR("recovery timed out, aborting\n"); + CERROR("%s: recovery timed out, aborting\n", obd->obd_name); spin_lock_bh(&obd->obd_processing_task_lock); if (obd->obd_recovering) obd->obd_abort_recovery = 1; @@ -1353,60 +1360,4 @@ void target_committed_to_req(struct ptlrpc_request *req) obd->obd_last_committed, req->rq_xid); } -int target_handle_qc_callback(struct ptlrpc_request *req) -{ - struct obd_quotactl *oqctl; - struct client_obd *cli = &req->rq_export->exp_obd->u.cli; - - oqctl = lustre_swab_reqbuf(req, 0, sizeof(*oqctl), - lustre_swab_obd_quotactl); - - spin_lock(&cli->cl_qchk_lock); - cli->cl_qchk_stat = oqctl->qc_stat; - spin_unlock(&cli->cl_qchk_lock); - - return 0; -} - -int target_handle_dqacq_callback(struct ptlrpc_request *req) -{ - struct obd_device *obd = req->rq_export->exp_obd; - struct obd_device *master_obd; - struct lustre_quota_ctxt *qctxt; - struct qunit_data *qdata, *rep; - int rc = 0, repsize = sizeof(struct qunit_data); - ENTRY; - - rc = lustre_pack_reply(req, 1, &repsize, NULL); - if (rc) { - CERROR("packing reply failed!: rc = %d\n", rc); - RETURN(rc); - } - rep = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*rep)); - LASSERT(rep); - - qdata = lustre_swab_reqbuf(req, 0, sizeof(*qdata), lustre_swab_qdata); - if (qdata == NULL) { - CERROR("unpacking request buffer failed!"); - RETURN(-EPROTO); - } - - /* we use the observer */ - LASSERT(obd->obd_observer && obd->obd_observer->obd_observer); - master_obd = obd->obd_observer->obd_observer; - qctxt = &master_obd->u.mds.mds_quota_ctxt; - - LASSERT(qctxt->lqc_handler); - rc = qctxt->lqc_handler(master_obd, qdata, req->rq_reqmsg->opc); - if (rc && rc != -EDQUOT) - CERROR("dqacq failed! (rc:%d)\n", rc); - - /* the qd_count might be changed in lqc_handler */ - memcpy(rep, qdata, sizeof(*rep)); - req->rq_status = rc; - rc = ptlrpc_reply(req); - - RETURN(rc); -} - EXPORT_SYMBOL(target_committed_to_req); diff --git a/lustre/ldlm/ldlm_lock.c b/lustre/ldlm/ldlm_lock.c index e846779..b38c3b5 100644 --- a/lustre/ldlm/ldlm_lock.c +++ b/lustre/ldlm/ldlm_lock.c @@ -45,7 +45,8 @@ char *ldlm_lockname[] = { [LCK_PR] "PR", [LCK_CW] "CW", [LCK_CR] "CR", - [LCK_NL] "NL" + [LCK_NL] "NL", + [LCK_GROUP] "GROUP" }; char *ldlm_typename[] = { [LDLM_PLAIN] "PLN", @@ -122,6 +123,7 @@ void ldlm_lock_put(struct ldlm_lock *lock) if (atomic_dec_and_test(&lock->l_refc)) { struct ldlm_namespace *ns = lock->l_resource->lr_namespace; + struct obd_export *export = NULL; l_lock(&ns->ns_lock); LDLM_DEBUG(lock, "final lock_put on destroyed lock, freeing"); @@ -134,8 +136,7 @@ void ldlm_lock_put(struct ldlm_lock *lock) ldlm_resource_putref(lock->l_resource); lock->l_resource = NULL; - if (lock->l_export) - class_export_put(lock->l_export); + export = lock->l_export; if (lock->l_parent) LDLM_LOCK_PUT(lock->l_parent); @@ -145,6 +146,8 @@ void ldlm_lock_put(struct ldlm_lock *lock) OBD_SLAB_FREE(lock, ldlm_lock_slab, sizeof(*lock)); l_unlock(&ns->ns_lock); + if (export) + class_export_put(export); } EXIT; @@ -155,6 +158,7 @@ void ldlm_lock_remove_from_lru(struct ldlm_lock *lock) ENTRY; l_lock(&lock->l_resource->lr_namespace->ns_lock); if (!list_empty(&lock->l_lru)) { + LASSERT(lock->l_resource->lr_type != LDLM_FLOCK); list_del_init(&lock->l_lru); lock->l_resource->lr_namespace->ns_nr_unused--; LASSERT(lock->l_resource->lr_namespace->ns_nr_unused >= 0); @@ -248,6 +252,7 @@ static struct ldlm_lock *ldlm_lock_new(struct ldlm_lock *parent, atomic_set(&lock->l_refc, 2); INIT_LIST_HEAD(&lock->l_children); + INIT_LIST_HEAD(&lock->l_childof); INIT_LIST_HEAD(&lock->l_res_link); INIT_LIST_HEAD(&lock->l_lru); INIT_LIST_HEAD(&lock->l_export_chain); @@ -433,7 +438,7 @@ void ldlm_lock_addref_internal(struct ldlm_lock *lock, __u32 mode) ldlm_lock_remove_from_lru(lock); if (mode & (LCK_NL | LCK_CR | LCK_PR)) lock->l_readers++; - if (mode & (LCK_EX | LCK_CW | LCK_PW)) + if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP)) lock->l_writers++; lock->l_last_used = jiffies; LDLM_LOCK_GET(lock); @@ -453,7 +458,7 @@ void ldlm_lock_decref_internal(struct ldlm_lock *lock, __u32 mode) LASSERT(lock->l_readers > 0); lock->l_readers--; } - if (mode & (LCK_EX | LCK_CW | LCK_PW)) { + if (mode & (LCK_EX | LCK_CW | LCK_PW | LCK_GROUP)) { LASSERT(lock->l_writers > 0); lock->l_writers--; } @@ -588,6 +593,11 @@ static struct ldlm_lock *search_queue(struct list_head *queue, ldlm_mode_t mode, lock->l_policy_data.l_extent.end < policy->l_extent.end)) continue; + if (unlikely(mode == LCK_GROUP) && + lock->l_resource->lr_type == LDLM_EXTENT && + lock->l_policy_data.l_extent.gid != policy->l_extent.gid) + continue; + if (lock->l_destroyed || (lock->l_flags & LDLM_FL_FAILED)) continue; @@ -923,7 +933,6 @@ int ldlm_run_ast_work(struct ldlm_namespace *ns, struct list_head *rpc_list) (w->w_lock, &w->w_desc, w->w_data, LDLM_CB_BLOCKING); } else if (w->w_lock->l_completion_ast != NULL) { - LASSERT(w->w_lock->l_completion_ast != NULL); rc = w->w_lock->l_completion_ast(w->w_lock, w->w_flags, w->w_data); } else { @@ -1090,15 +1099,24 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode, struct ldlm_resource *res; struct ldlm_namespace *ns; int granted = 0; + int old_mode, rc; + ldlm_error_t err; ENTRY; - LBUG(); + if (new_mode == lock->l_granted_mode) { // No changes? Just return. + *flags |= LDLM_FL_BLOCK_GRANTED; + RETURN(lock->l_resource); + } + + LASSERTF(new_mode == LCK_PW && lock->l_granted_mode == LCK_PR, + "new_mode %u, granted %u\n", new_mode, lock->l_granted_mode); res = lock->l_resource; ns = res->lr_namespace; l_lock(&ns->ns_lock); + old_mode = lock->l_req_mode; lock->l_req_mode = new_mode; ldlm_resource_unlink_lock(lock); @@ -1109,6 +1127,8 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode, } else { /* This should never happen, because of the way the * server handles conversions. */ + LDLM_ERROR(lock, "Erroneous flags %d on local lock\n", + *flags); LBUG(); res->lr_tmp = &rpc_list; @@ -1120,10 +1140,20 @@ struct ldlm_resource *ldlm_lock_convert(struct ldlm_lock *lock, int new_mode, lock->l_completion_ast(lock, 0, NULL); } } else { - /* FIXME: We should try the conversion right away and possibly - * return success without the need for an extra AST */ - ldlm_resource_add_lock(res, &res->lr_converting, lock); - *flags |= LDLM_FL_BLOCK_CONV; + int pflags = 0; + ldlm_processing_policy policy; + policy = ldlm_processing_policy_table[res->lr_type]; + res->lr_tmp = &rpc_list; + rc = policy(lock, &pflags, 0, &err); + res->lr_tmp = NULL; + if (rc == LDLM_ITER_STOP) { + lock->l_req_mode = old_mode; + ldlm_resource_add_lock(res, &res->lr_granted, lock); + res = NULL; + } else { + *flags |= LDLM_FL_BLOCK_GRANTED; + granted = 1; + } } l_unlock(&ns->ns_lock); diff --git a/lustre/ldlm/ldlm_lockd.c b/lustre/ldlm/ldlm_lockd.c index 1d7030e..875fcd6 100644 --- a/lustre/ldlm/ldlm_lockd.c +++ b/lustre/ldlm/ldlm_lockd.c @@ -135,7 +135,8 @@ static int expired_lock_main(void *arg) /* from waiting_locks_callback, but not in timer */ portals_debug_dumplog(); - portals_run_lbug_upcall(__FILE__, "waiting_locks_cb", + portals_run_lbug_upcall(__FILE__, + "waiting_locks_callback", expired_lock_thread.elt_dump); spin_lock_bh(&waiting_locks_spinlock); @@ -150,8 +151,8 @@ static int expired_lock_main(void *arg) l_pending_chain); if ((void *)lock < LP_POISON + PAGE_SIZE && (void *)lock >= LP_POISON) { - CERROR("free lock on elt list %p\n", lock); spin_unlock_bh(&waiting_locks_spinlock); + CERROR("free lock on elt list %p\n", lock); LBUG(); } list_del_init(&lock->l_pending_chain); @@ -166,7 +167,7 @@ static int expired_lock_main(void *arg) export = class_export_get(lock->l_export); spin_unlock_bh(&waiting_locks_spinlock); - ptlrpc_fail_export(export); + class_fail_export(export); class_export_put(export); spin_lock_bh(&waiting_locks_spinlock); } @@ -186,12 +187,16 @@ static void waiting_locks_callback(unsigned long unused) { struct ldlm_lock *lock, *last = NULL; + if (obd_dump_on_timeout) + portals_debug_dumplog(); + spin_lock_bh(&waiting_locks_spinlock); while (!list_empty(&waiting_locks_list)) { lock = list_entry(waiting_locks_list.next, struct ldlm_lock, l_pending_chain); - if (time_after(lock->l_callback_timeout, jiffies)) + if (time_after(lock->l_callback_timeout, jiffies) || + (lock->l_req_mode == LCK_GROUP)) break; LDLM_ERROR(lock, "lock callback timer expired: evicting client " @@ -363,7 +368,9 @@ static void ldlm_failed_ast(struct ldlm_lock *lock, int rc, " (%s)", ast_type, rc, lock->l_export->exp_client_uuid.uuid, conn->c_remote_uuid.uuid, conn->c_peer.nid, str); - ptlrpc_fail_export(lock->l_export); + if (obd_dump_on_timeout) + portals_debug_dumplog(); + class_fail_export(lock->l_export); } static int ldlm_handle_ast_error(struct ldlm_lock *lock, @@ -387,6 +394,7 @@ static int ldlm_handle_ast_error(struct ldlm_lock *lock, ldlm_failed_ast(lock, rc, ast_type); } } else if (rc) { + l_lock(&lock->l_resource->lr_namespace->ns_lock); if (rc == -EINVAL) LDLM_DEBUG(lock, "client (nid %s) returned %d" " from %s AST - normal race", @@ -398,6 +406,7 @@ static int ldlm_handle_ast_error(struct ldlm_lock *lock, (req->rq_repmsg != NULL) ? req->rq_repmsg->status : 0, ast_type); ldlm_lock_cancel(lock); + l_unlock(&lock->l_resource->lr_namespace->ns_lock); /* Server-side AST functions are called from ldlm_reprocess_all, * which needs to be told to please restart its reprocessing. */ rc = -ERESTART; @@ -495,10 +504,11 @@ int ldlm_server_completion_ast(struct ldlm_lock *lock, int flags, void *data) LASSERT(lock != NULL); do_gettimeofday(&granted_time); - total_enqueue_wait = timeval_sub(&granted_time, &lock->l_enqueued_time); + total_enqueue_wait = timeval_sub(&granted_time,&lock->l_enqueued_time); if (total_enqueue_wait / 1000000 > obd_timeout) - LDLM_ERROR(lock, "enqueue wait took %luus", total_enqueue_wait); + LDLM_ERROR(lock, "enqueue wait took %luus from %lu", + total_enqueue_wait, lock->l_enqueued_time.tv_sec); down(&lock->l_resource->lr_lvb_sem); if (lock->l_resource->lr_lvb_len) { @@ -643,7 +653,7 @@ int ldlm_handle_enqueue(struct ptlrpc_request *req, LASSERT(req->rq_export); if (flags & LDLM_FL_REPLAY) { - lock = find_existing_lock(req->rq_export, + lock = find_existing_lock(req->rq_export, &dlm_req->lock_handle1); if (lock != NULL) { DEBUG_REQ(D_HA, req, "found existing lock cookie "LPX64, @@ -659,8 +669,8 @@ int ldlm_handle_enqueue(struct ptlrpc_request *req, GOTO(out, rc = -EFAULT); } - if (dlm_req->lock_desc.l_req_mode < LCK_EX || - dlm_req->lock_desc.l_req_mode > LCK_NL || + if (dlm_req->lock_desc.l_req_mode <= LCK_MINMODE || + dlm_req->lock_desc.l_req_mode >= LCK_MAXMODE || dlm_req->lock_desc.l_req_mode & (dlm_req->lock_desc.l_req_mode-1)) { DEBUG_REQ(D_ERROR, req, "invalid lock request mode %d\n", dlm_req->lock_desc.l_req_mode); @@ -742,7 +752,8 @@ existing_lock: l_lock(&lock->l_resource->lr_namespace->ns_lock); /* Don't move a pending lock onto the export if it has already * been evicted. Cancel it now instead. (bug 5683) */ - if (req->rq_export->exp_failed) { + if (req->rq_export->exp_failed || + OBD_FAIL_CHECK_ONCE(OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT)) { LDLM_ERROR(lock, "lock on destroyed export %p", req->rq_export); rc = -ENOTCONN; } else if (lock->l_flags & LDLM_FL_AST_SENT) { @@ -784,6 +795,7 @@ existing_lock: } up(&lock->l_resource->lr_lvb_sem); } else { + ldlm_resource_unlink_lock(lock); ldlm_lock_destroy(lock); } @@ -824,18 +836,28 @@ int ldlm_handle_convert(struct ptlrpc_request *req) if (!lock) { req->rq_status = EINVAL; } else { + void *res; l_lock(&lock->l_resource->lr_namespace->ns_lock); LDLM_DEBUG(lock, "server-side convert handler START"); - ldlm_lock_convert(lock, dlm_req->lock_desc.l_req_mode, - &dlm_rep->lock_flags); - if (ldlm_del_waiting_lock(lock)) - CDEBUG(D_DLMTRACE, "converted waiting lock %p\n", lock); l_unlock(&lock->l_resource->lr_namespace->ns_lock); - req->rq_status = 0; + do_gettimeofday(&lock->l_enqueued_time); + res = ldlm_lock_convert(lock, dlm_req->lock_desc.l_req_mode, + &dlm_rep->lock_flags); + if (res) { + l_lock(&lock->l_resource->lr_namespace->ns_lock); + if (ldlm_del_waiting_lock(lock)) + CDEBUG(D_DLMTRACE,"converted waiting lock %p\n", + lock); + l_unlock(&lock->l_resource->lr_namespace->ns_lock); + req->rq_status = 0; + } else { + req->rq_status = EDEADLOCK; + } } if (lock) { - ldlm_reprocess_all(lock->l_resource); + if (!req->rq_status) + ldlm_reprocess_all(lock->l_resource); l_lock(&lock->l_resource->lr_namespace->ns_lock); LDLM_DEBUG(lock, "server-side convert handler END"); l_unlock(&lock->l_resource->lr_namespace->ns_lock); @@ -1393,7 +1415,7 @@ static int ldlm_setup(void) if (ldlm_state == NULL) RETURN(-ENOMEM); -#ifdef __KERNEL__ +#ifdef LPROCFS rc = ldlm_proc_setup(); if (rc != 0) GOTO(out_free, rc); @@ -1487,7 +1509,7 @@ static int ldlm_setup(void) #endif out_proc: -#ifdef __KERNEL__ +#ifdef LPROCFS ldlm_proc_cleanup(); out_free: #endif @@ -1532,7 +1554,9 @@ static int ldlm_cleanup(int force) wake_up(&expired_lock_thread.elt_waitq); wait_event(expired_lock_thread.elt_waitq, expired_lock_thread.elt_state == ELT_STOPPED); - +#else + ptlrpc_unregister_service(ldlm_state->ldlm_cb_service); + ptlrpc_unregister_service(ldlm_state->ldlm_cancel_service); #endif OBD_FREE(ldlm_state, sizeof(*ldlm_state)); @@ -1572,9 +1596,6 @@ void __exit ldlm_exit(void) "couldn't free ldlm lock slab\n"); } -/* ldlm_flock.c */ -EXPORT_SYMBOL(ldlm_flock_completion_ast); - /* ldlm_extent.c */ EXPORT_SYMBOL(ldlm_extent_shift_kms); diff --git a/lustre/ldlm/ldlm_request.c b/lustre/ldlm/ldlm_request.c index ff05942..f88f0ef 100644 --- a/lustre/ldlm/ldlm_request.c +++ b/lustre/ldlm/ldlm_request.c @@ -86,8 +86,10 @@ int ldlm_completion_ast(struct ldlm_lock *lock, int flags, void *data) int rc = 0; ENTRY; - if (flags == LDLM_FL_WAIT_NOREPROC) + if (flags == LDLM_FL_WAIT_NOREPROC) { + LDLM_DEBUG(lock, "client-side enqueue waiting on pending lock"); goto noreproc; + } if (!(flags & (LDLM_FL_BLOCK_WAIT | LDLM_FL_BLOCK_GRANTED | LDLM_FL_BLOCK_CONV))) { @@ -110,8 +112,16 @@ noreproc: lwd.lwd_lock = lock; - lwi = LWI_TIMEOUT_INTR(obd_timeout * HZ, ldlm_expired_completion_wait, - interrupted_completion_wait, &lwd); + if (unlikely(flags & LDLM_FL_NO_TIMEOUT)) { + LDLM_DEBUG(lock, "waiting indefinitely because CW lock was" + " met\n"); + lwi = LWI_INTR(interrupted_completion_wait, &lwd); + } else { + lwi = LWI_TIMEOUT_INTR(obd_timeout * HZ, + ldlm_expired_completion_wait, + interrupted_completion_wait, &lwd); + } + if (imp != NULL) { spin_lock_irqsave(&imp->imp_lock, irqflags); lwd.lwd_generation = imp->imp_generation; @@ -210,6 +220,12 @@ static void failed_lock_cleanup(struct ldlm_namespace *ns, l_unlock(&ns->ns_lock); ldlm_lock_decref_and_cancel(lockh, mode); + + /* XXX - HACK because we shouldn't call ldlm_lock_destroy() + * from llite/file.c/ll_file_flock(). */ + if (lock->l_resource->lr_type == LDLM_FLOCK) { + ldlm_lock_destroy(lock); + } } int ldlm_cli_enqueue(struct obd_export *exp, @@ -433,6 +449,8 @@ cleanup: static int ldlm_cli_convert_local(struct ldlm_lock *lock, int new_mode, int *flags) { + struct ldlm_resource *res; + int rc; ENTRY; if (lock->l_resource->lr_namespace->ns_client) { CERROR("Trying to cancel local lock\n"); @@ -440,16 +458,22 @@ static int ldlm_cli_convert_local(struct ldlm_lock *lock, int new_mode, } LDLM_DEBUG(lock, "client-side local convert"); - ldlm_lock_convert(lock, new_mode, flags); - ldlm_reprocess_all(lock->l_resource); - + res = ldlm_lock_convert(lock, new_mode, flags); + if (res) { + ldlm_reprocess_all(res); + rc = 0; + } else { + rc = EDEADLOCK; + } LDLM_DEBUG(lock, "client-side local convert handler END"); LDLM_LOCK_PUT(lock); - RETURN(0); + RETURN(rc); } /* FIXME: one of ldlm_cli_convert or the server side should reject attempted * conversion of locks which are on the waiting or converting queue */ +/* Caller of this code is supposed to take care of lock readers/writers + accounting */ int ldlm_cli_convert(struct lustre_handle *lockh, int new_mode, int *flags) { struct ldlm_request *body; @@ -498,13 +522,23 @@ int ldlm_cli_convert(struct lustre_handle *lockh, int new_mode, int *flags) GOTO (out, rc = -EPROTO); } + if (req->rq_status) + GOTO(out, rc = req->rq_status); + res = ldlm_lock_convert(lock, new_mode, &reply->lock_flags); - if (res != NULL) + if (res != NULL) { ldlm_reprocess_all(res); - /* Go to sleep until the lock is granted. */ - /* FIXME: or cancelled. */ - if (lock->l_completion_ast) - lock->l_completion_ast(lock, LDLM_FL_WAIT_NOREPROC, NULL); + /* Go to sleep until the lock is granted. */ + /* FIXME: or cancelled. */ + if (lock->l_completion_ast) { + rc = lock->l_completion_ast(lock, LDLM_FL_WAIT_NOREPROC, + NULL); + if (rc) + GOTO(out, rc); + } + } else { + rc = EDEADLOCK; + } EXIT; out: LDLM_LOCK_PUT(lock); @@ -612,6 +646,10 @@ int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync) LIST_HEAD(cblist); ENTRY; +#ifndef __KERNEL__ + sync = LDLM_SYNC; /* force to be sync in user space */ +#endif + l_lock(&ns->ns_lock); count = ns->ns_nr_unused - ns->ns_max_unused; @@ -635,18 +673,25 @@ int ldlm_cancel_lru(struct ldlm_namespace *ns, ldlm_sync_t sync) /* We can't re-add to l_lru as it confuses the refcounting in * ldlm_lock_remove_from_lru() if an AST arrives after we drop - * ns_lock below. Use l_pending_chain as that is unused on - * client, and lru is client-only. bug 5666 */ - if (sync != LDLM_ASYNC || ldlm_bl_to_thread(ns, NULL, lock)) - list_add(&lock->l_pending_chain, &cblist); + * ns_lock below. Use l_export_chain as that is unused on + * client, and lru is client-only (l_pending_chain is used by + * ldlm_chain_lock_for_replay() on client). bug 5666 */ + if (sync != LDLM_ASYNC || ldlm_bl_to_thread(ns, NULL, lock)) { + LASSERTF(list_empty(&lock->l_export_chain), + "lock %p next %p prev %p\n", + lock, &lock->l_export_chain.next, + &lock->l_export_chain.prev); + __LDLM_DEBUG(D_INFO, lock, "adding to LRU clear list"); + list_add(&lock->l_export_chain, &cblist); + } if (--count == 0) break; } l_unlock(&ns->ns_lock); - list_for_each_entry_safe(lock, next, &cblist, l_pending_chain) { - list_del_init(&lock->l_pending_chain); + list_for_each_entry_safe(lock, next, &cblist, l_export_chain) { + list_del_init(&lock->l_export_chain); ldlm_handle_bl_callback(ns, NULL, lock); } RETURN(rc); @@ -934,6 +979,8 @@ static int ldlm_chain_lock_for_replay(struct ldlm_lock *lock, void *closure) struct list_head *list = closure; /* we use l_pending_chain here, because it's unused on clients. */ + LASSERTF(list_empty(&lock->l_pending_chain),"lock %p next %p prev %p\n", + lock, &lock->l_pending_chain.next,&lock->l_pending_chain.prev); list_add(&lock->l_pending_chain, list); return LDLM_ITER_CONTINUE; } @@ -1035,8 +1082,8 @@ static int replay_one_lock(struct obd_import *imp, struct ldlm_lock *lock) int ldlm_replay_locks(struct obd_import *imp) { struct ldlm_namespace *ns = imp->imp_obd->obd_namespace; - struct list_head list, *pos, *next; - struct ldlm_lock *lock; + struct list_head list; + struct ldlm_lock *lock, *next; int rc = 0; ENTRY; @@ -1050,11 +1097,11 @@ int ldlm_replay_locks(struct obd_import *imp) l_lock(&ns->ns_lock); (void)ldlm_namespace_foreach(ns, ldlm_chain_lock_for_replay, &list); - list_for_each_safe(pos, next, &list) { - lock = list_entry(pos, struct ldlm_lock, l_pending_chain); - rc = replay_one_lock(imp, lock); + list_for_each_entry_safe(lock, next, &list, l_pending_chain) { + list_del_init(&lock->l_pending_chain); if (rc) - break; /* or try to do the rest? */ + continue; /* or try to do the rest? */ + rc = replay_one_lock(imp, lock); } l_unlock(&ns->ns_lock); diff --git a/lustre/ldlm/ldlm_resource.c b/lustre/ldlm/ldlm_resource.c index b362344..9a5922b 100644 --- a/lustre/ldlm/ldlm_resource.c +++ b/lustre/ldlm/ldlm_resource.c @@ -39,7 +39,7 @@ struct proc_dir_entry *ldlm_type_proc_dir = NULL; struct proc_dir_entry *ldlm_ns_proc_dir = NULL; struct proc_dir_entry *ldlm_svc_proc_dir = NULL; -#ifdef __KERNEL__ +#ifdef LPROCFS static int ldlm_proc_dump_ns(struct file *file, const char *buffer, unsigned long count, void *data) { @@ -206,8 +206,10 @@ void ldlm_proc_namespace(struct ldlm_namespace *ns) lprocfs_add_vars(ldlm_ns_proc_dir, lock_vars, 0); } } -#endif #undef MAX_STRING_SIZE +#else +#define ldlm_proc_namespace(ns) do {} while (0) +#endif /* LPROCFS */ struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 client) { @@ -255,9 +257,7 @@ struct ldlm_namespace *ldlm_namespace_new(char *name, __u32 client) down(&ldlm_namespace_lock); list_add(&ns->ns_list_chain, &ldlm_namespace_list); up(&ldlm_namespace_lock); -#ifdef __KERNEL__ ldlm_proc_namespace(ns); -#endif RETURN(ns); out_hash: @@ -356,7 +356,7 @@ int ldlm_namespace_cleanup(struct ldlm_namespace *ns, int flags) if (!ldlm_resource_putref(res)) { CERROR("Namespace %s resource refcount %d " - "after lock cleanup\n", + "after lock cleanup; forcing cleanup.\n", ns->ns_name, atomic_read(&res->lr_refcount)); } @@ -380,7 +380,7 @@ int ldlm_namespace_free(struct ldlm_namespace *ns, int force) /* At shutdown time, don't call the cancellation callback */ ldlm_namespace_cleanup(ns, 0); -#ifdef __KERNEL__ +#ifdef LPROCFS { struct proc_dir_entry *dir; dir = lprocfs_srch(ldlm_ns_proc_dir, ns->ns_name); @@ -396,19 +396,20 @@ int ldlm_namespace_free(struct ldlm_namespace *ns, int force) if (atomic_read(&ns->ns_refcount) > 0) { struct l_wait_info lwi = LWI_INTR(NULL, NULL); int rc; - CDEBUG(D_DLMTRACE, - "dlm namespace %s free waiting on refcount %d\n", + CDEBUG(D_DLMTRACE, + "dlm namespace %s free waiting on refcount %d\n", ns->ns_name, atomic_read(&ns->ns_refcount)); rc = l_wait_event(ns->ns_refcount_waitq, atomic_read(&ns->ns_refcount) == 0, &lwi); if (atomic_read(&ns->ns_refcount)) { - CERROR("Lock manager: waiting for the %s namespace " - "was aborted with %d resources in use. (%d)\n" - "I'm going to try to clean up anyway, but I " - "might require a reboot of this node.\n", - ns->ns_name, atomic_read(&ns->ns_refcount), rc); + LCONSOLE_ERROR("Lock manager: wait for %s namespace " + "cleanup aborted with %d resources in " + "use. (%d)\nI'm going to try to clean " + "up anyway, but I might need a reboot " + "of this node.\n", ns->ns_name, + atomic_read(&ns->ns_refcount), rc); } - CDEBUG(D_DLMTRACE, + CDEBUG(D_DLMTRACE, "dlm namespace %s free done waiting\n", ns->ns_name); } @@ -545,8 +546,8 @@ ldlm_resource_get(struct ldlm_namespace *ns, struct ldlm_resource *parent, rc = ns->ns_lvbo->lvbo_init(res); up(&res->lr_lvb_sem); if (rc) - CERROR("lvbo_init failed for resource "LPU64": rc %d\n", - name.name[0], rc); + CERROR("lvbo_init failed for resource "LPU64"/"LPU64 + ": rc %d\n", name.name[0], name.name[1], rc); } else { out: l_unlock(&ns->ns_lock); @@ -649,6 +650,29 @@ void ldlm_resource_add_lock(struct ldlm_resource *res, struct list_head *head, l_unlock(&res->lr_namespace->ns_lock); } +void ldlm_resource_insert_lock_after(struct ldlm_lock *original, + struct ldlm_lock *new) +{ + struct ldlm_resource *res = original->l_resource; + + l_lock(&res->lr_namespace->ns_lock); + + ldlm_resource_dump(D_OTHER, res); + CDEBUG(D_OTHER, "About to insert this lock after %p:\n", original); + ldlm_lock_dump(D_OTHER, new, 0); + + if (new->l_destroyed) { + CDEBUG(D_OTHER, "Lock destroyed, not adding to resource\n"); + goto out; + } + + LASSERT(list_empty(&new->l_res_link)); + + list_add(&new->l_res_link, &original->l_res_link); + out: + l_unlock(&res->lr_namespace->ns_lock); +} + void ldlm_resource_unlink_lock(struct ldlm_lock *lock) { l_lock(&lock->l_resource->lr_namespace->ns_lock); diff --git a/lustre/liblustre/Makefile.am b/lustre/liblustre/Makefile.am index a776768..6fac0b2 100644 --- a/lustre/liblustre/Makefile.am +++ b/lustre/liblustre/Makefile.am @@ -1,10 +1,11 @@ ## Liblustre excecutables & libraries Makefile SUBDIRS = . tests -AM_CPPFLAGS = $(HAVE_EFENCE) -I$(SYSIO)/include -D_LARGEFILE64_SOURCE=1 $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals +AM_CPPFLAGS = $(HAVE_EFENCE) -I$(SYSIO)/include -D_LARGEFILE64_SOURCE=1 \ + $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals AM_CFLAGS = $(LLCFLAGS) -LIBS = $(LIBEFENCE) +AM_LIBS = $(LIBEFENCE) LUSTRE_LIBS = libllite.a \ $(top_builddir)/lustre/lov/liblov.a \ @@ -15,14 +16,16 @@ LUSTRE_LIBS = libllite.a \ $(top_builddir)/lustre/obdclass/liblustreclass.a \ $(top_builddir)/lustre/lvfs/liblvfs.a +if !CRAY_PORTALS PTL_LIBS = $(top_builddir)/portals/utils/libuptlctl.a \ $(top_builddir)/portals/unals/libtcpnal.a \ $(top_builddir)/portals/portals/libportals.a +else +PTL_LIBS = $(top_builddir)/portals/utils/libuptlctl.a \ + $(CRAY_PORTALS_LIBS)/libportals.a +endif -SYSIO_LIBS = $(SYSIO)/drivers/native/libsysio_native.a \ - $(SYSIO)/drivers/sockets/libsysio_sockets.a \ - $(SYSIO)/src/libsysio.a \ - $(SYSIO)/dev/stdfd/libsysio_stdfd.a +SYSIO_LIBS = $(SYSIO)/lib/libsysio.a if LIBLUSTRE lib_LIBRARIES = liblustre.a @@ -40,16 +43,18 @@ install-exec-hook: liblustre.so done else install-exec-hook: - endif -libllite_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c llite_lib.h +libllite_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c \ + lutil.c lutil.h llite_lib.h # for make rpms -- need cleanup liblustre_a_SOURCES = llite_lib.c super.c namei.c rw.c file.c dir.c \ llite_lib.h liblustre.a : $(LUSTRE_LIBS) $(PTL_LIBS) $(SYSIO_LIBS) - $(srcdir)/genlib.sh $(SYSIO) $(AR) $(LINK) || ( rm -f $@; exit 1 ) + sh $(srcdir)/genlib.sh "$(SYSIO)" "$(CRAY_PORTALS_LIBS)" "$(LIBS)" EXTRA_DIST = genlib.sh + +CLEANFILES := liblsupport.a liblustre.so diff --git a/lustre/liblustre/dir.c b/lustre/liblustre/dir.c index c125b79..ec33ac3 100644 --- a/lustre/liblustre/dir.c +++ b/lustre/liblustre/dir.c @@ -3,7 +3,7 @@ * * Lustre Light directory handling * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -33,23 +33,39 @@ #include <sys/fcntl.h> #include <sys/queue.h> +#ifdef HAVE_XTIO_H +#include <xtio.h> +#endif #include <sysio.h> #include <fs.h> #include <mount.h> #include <inode.h> +#ifdef HAVE_FILE_H #include <file.h> +#endif #undef LIST_HEAD +#ifdef HAVE_LINUX_TYPES_H #include <linux/types.h> -#include <linux/dirent.h> +#elif defined(HAVE_SYS_TYPES_H) +#include <sys/types.h> +#endif + +#ifdef HAVE_LINUX_UNISTD_H #include <linux/unistd.h> +#elif defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include <dirent.h> #include "llite_lib.h" static int llu_dir_do_readpage(struct inode *inode, struct page *page) { struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct llu_sb_info *sbi = llu_i2sbi(inode); struct ll_fid mdc_fid; __u64 offset; @@ -61,19 +77,9 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page) struct mdc_op_data data; struct obd_device *obddev = class_exp2obd(sbi->ll_mdc_exp); struct ldlm_res_id res_id = - { .name = {lli->lli_st_ino, (__u64)lli->lli_st_generation} }; + { .name = {st->st_ino, (__u64)lli->lli_st_generation} }; ENTRY; - if ((lli->lli_st_size + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT <= page->index) { - /* XXX why do we need this exactly, and why do we think that - * an all-zero directory page is useful? - */ - CERROR("memsetting dir page %lu to zero (size %lld)\n", - page->index, lli->lli_st_size); - memset(page->addr, 0, PAGE_CACHE_SIZE); - GOTO(readpage_out, rc); - } - rc = ldlm_lock_match(obddev->obd_namespace, LDLM_FL_BLOCK_GRANTED, &res_id, LDLM_PLAIN, NULL, LCK_PR, &lockh); if (!rc) { @@ -93,7 +99,7 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page) } ldlm_lock_dump_handle(D_OTHER, &lockh); - mdc_pack_fid(&mdc_fid, lli->lli_st_ino, lli->lli_st_generation, S_IFDIR); + mdc_pack_fid(&mdc_fid, st->st_ino, lli->lli_st_generation, S_IFDIR); offset = page->index << PAGE_SHIFT; rc = mdc_readpage(sbi->ll_mdc_exp, &mdc_fid, @@ -103,12 +109,13 @@ static int llu_dir_do_readpage(struct inode *inode, struct page *page) LASSERT (body != NULL); /* checked by mdc_readpage() */ LASSERT_REPSWABBED (request, 0); /* swabbed by mdc_readpage() */ - lli->lli_st_size = body->size; + st->st_size = body->size; + } else { + CERROR("read_dir_page(%ld) error %d\n", page->index, rc); } ptlrpc_req_finished(request); EXIT; - readpage_out: ldlm_lock_decref(&lockh, LCK_PR); return rc; } @@ -135,6 +142,29 @@ static struct page *llu_dir_read_page(struct inode *ino, int pgidx) return page; } +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; + +static unsigned char ext2_filetype_table[EXT2_FT_MAX] = { + [EXT2_FT_UNKNOWN] DT_UNKNOWN, + [EXT2_FT_REG_FILE] DT_REG, + [EXT2_FT_DIR] DT_DIR, + [EXT2_FT_CHRDEV] DT_CHR, + [EXT2_FT_BLKDEV] DT_BLK, + [EXT2_FT_FIFO] DT_FIFO, + [EXT2_FT_SOCK] DT_SOCK, + [EXT2_FT_SYMLINK] DT_LNK, +}; + #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) #define ROUND_UP64(x) (((x)+sizeof(__u64)-1) & ~(sizeof(__u64)-1)) @@ -165,16 +195,24 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes, _SYSIO_OFF_T *basep) { struct llu_inode_info *lli = llu_i2info(ino); + struct intnl_stat *st = llu_i2stat(ino); loff_t pos = *basep, offset; int maxpages, pgidx, filled = 0; ENTRY; + if (st->st_size == 0) { + CWARN("dir size is 0?\n"); + RETURN(0); + } + + liblustre_wait_event(0); + if (pos == -1) pos = lli->lli_dir_pos; - maxpages = lli->lli_st_size >> PAGE_CACHE_SHIFT; - pgidx = pos >> PAGE_CACHE_SHIFT; - offset = pos & ~PAGE_CACHE_MASK; + maxpages = (st->st_size + PAGE_SIZE - 1) >> PAGE_SHIFT; + pgidx = pos >> PAGE_SHIFT; + offset = pos & ~PAGE_MASK; for ( ; pgidx < maxpages ; pgidx++, offset = 0) { struct page *page; @@ -186,23 +224,24 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes, continue; /* size might have been updated by mdc_readpage */ - maxpages = lli->lli_st_size >> PAGE_CACHE_SHIFT; + maxpages = (st->st_size + PAGE_SIZE - 1) >> PAGE_SHIFT; /* fill in buffer */ addr = page->addr; - limit = addr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1); + limit = addr + PAGE_SIZE - EXT2_DIR_REC_LEN(1); de = (struct ext2_dirent *) (addr + offset); for ( ; (char*) de <= limit; de = ext2_next_entry(de)) { if (de->inode) { int over; - unsigned char d_type = 0; + unsigned char d_type = DT_UNKNOWN; - /* XXX handle type, etc here */ + if (de->file_type < EXT2_FT_MAX) + d_type = ext2_filetype_table[de->file_type]; offset = (char*) de - addr; over = filldir(buf, nbytes, de->name, de->name_len, - (pgidx << PAGE_CACHE_SHIFT) | offset, + (pgidx << PAGE_SHIFT) | offset, le32_to_cpu(de->inode), d_type, &filled); if (over) { free_page(page); @@ -214,7 +253,7 @@ ssize_t llu_iop_getdirentries(struct inode *ino, char *buf, size_t nbytes, free_page(page); } done: - lli->lli_dir_pos = pgidx << PAGE_CACHE_SHIFT | offset; + lli->lli_dir_pos = pgidx << PAGE_SHIFT | offset; *basep = lli->lli_dir_pos; RETURN(filled); } diff --git a/lustre/liblustre/file.c b/lustre/liblustre/file.c index 0aa6687..b7f4b55 100644 --- a/lustre/liblustre/file.c +++ b/lustre/liblustre/file.c @@ -3,7 +3,7 @@ * * Lustre Light file operations * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -28,13 +28,20 @@ #include <assert.h> #include <time.h> #include <sys/types.h> +#include <sys/stat.h> #include <sys/queue.h> +#include <fcntl.h> +#ifdef HAVE_XTIO_H +#include <xtio.h> +#endif #include <sysio.h> #include <fs.h> #include <mount.h> #include <inode.h> +#ifdef HAVE_FILE_H #include <file.h> +#endif #undef LIST_HEAD @@ -66,29 +73,29 @@ void obdo_refresh_inode(struct inode *dst, struct obdo *src, obd_flag valid) { - struct llu_inode_info *lli = llu_i2info(dst); + struct intnl_stat *st = llu_i2stat(dst); valid &= src->o_valid; if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME)) - CDEBUG(D_INODE, "valid %x, cur time %lu/%lu, new %lu/%lu\n", - src->o_valid, LTIME_S(lli->lli_st_mtime), - LTIME_S(lli->lli_st_ctime), + CDEBUG(D_INODE,"valid "LPX64", cur time %lu/%lu, new %lu/%lu\n", + src->o_valid, LTIME_S(st->st_mtime), + LTIME_S(st->st_ctime), (long)src->o_mtime, (long)src->o_ctime); - if (valid & OBD_MD_FLATIME && src->o_atime > LTIME_S(lli->lli_st_atime)) - LTIME_S(lli->lli_st_atime) = src->o_atime; - if (valid & OBD_MD_FLMTIME && src->o_mtime > LTIME_S(lli->lli_st_mtime)) - LTIME_S(lli->lli_st_mtime) = src->o_mtime; - if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(lli->lli_st_ctime)) - LTIME_S(lli->lli_st_ctime) = src->o_ctime; - if (valid & OBD_MD_FLSIZE && src->o_size > lli->lli_st_size) - lli->lli_st_size = src->o_size; + if (valid & OBD_MD_FLATIME && src->o_atime > LTIME_S(st->st_atime)) + LTIME_S(st->st_atime) = src->o_atime; + if (valid & OBD_MD_FLMTIME && src->o_mtime > LTIME_S(st->st_mtime)) + LTIME_S(st->st_mtime) = src->o_mtime; + if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(st->st_ctime)) + LTIME_S(st->st_ctime) = src->o_ctime; + if (valid & OBD_MD_FLSIZE && src->o_size > st->st_size) + st->st_size = src->o_size; /* optimum IO size */ if (valid & OBD_MD_FLBLKSZ) - lli->lli_st_blksize = src->o_blksize; + st->st_blksize = src->o_blksize; /* allocation of space */ - if (valid & OBD_MD_FLBLOCKS && src->o_blocks > lli->lli_st_blocks) - lli->lli_st_blocks = src->o_blocks; + if (valid & OBD_MD_FLBLOCKS && src->o_blocks > st->st_blocks) + st->st_blocks = src->o_blocks; } static int llu_local_open(struct llu_inode_info *lli, struct lookup_intent *it) @@ -105,7 +112,7 @@ static int llu_local_open(struct llu_inode_info *lli, struct lookup_intent *it) /* already opened? */ if (lli->lli_open_count++) RETURN(0); - + LASSERT(!lli->lli_file_data); OBD_ALLOC(fd, sizeof(*fd)); @@ -126,6 +133,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode) { struct inode *inode = pnode->p_base->pb_ino; struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct ll_file_data *fd; struct ptlrpc_request *request; struct lookup_intent *it; @@ -133,11 +141,13 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode) int rc = 0; ENTRY; + liblustre_wait_event(0); + /* don't do anything for '/' */ if (llu_is_root_inode(inode)) RETURN(0); - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu\n", lli->lli_st_ino); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu\n", st->st_ino); LL_GET_INTENT(inode, it); if (!it->d.lustre.it_disposition) { @@ -152,7 +162,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode) if (rc) LBUG(); - if (!S_ISREG(lli->lli_st_mode)) + if (!S_ISREG(st->st_mode)) GOTO(out_release, rc = 0); fd = lli->lli_file_data; @@ -166,7 +176,7 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode) } fd->fd_flags &= ~O_LOV_DELAY_CREATE; - lli->lli_open_flags = flags; + lli->lli_open_flags = flags & ~(O_CREAT | O_EXCL | O_TRUNC); out_release: request = it->d.lustre.it_data; @@ -175,6 +185,22 @@ int llu_iop_open(struct pnode *pnode, int flags, mode_t mode) it->it_op_release(it); OBD_FREE(it, sizeof(*it)); + /* libsysio haven't doing anything for O_TRUNC. here we + * simply simulate it as open(...); truncate(...); + */ + if (rc == 0 && (flags & O_TRUNC) && + S_ISREG(st->st_mode)) { + struct iattr attr; + + memset(&attr, 0, sizeof(attr)); + attr.ia_size = 0; + attr.ia_valid |= ATTR_SIZE | ATTR_RAW; + rc = llu_setattr_raw(inode, &attr); + if (rc) { + CERROR("error %d truncate in open()\n", rc); + } + } + RETURN(rc); } @@ -251,6 +277,7 @@ int llu_objects_destroy(struct ptlrpc_request *request, struct inode *dir) int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode) { struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct ll_file_data *fd = lli->lli_file_data; struct ptlrpc_request *req = NULL; struct obd_client_handle *och = &fd->fd_mds_och; @@ -258,7 +285,15 @@ int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode) int rc, valid; ENTRY; - obdo.o_id = lli->lli_st_ino; + /* clear group lock, if present */ + if (fd->fd_flags & LL_FILE_GROUP_LOCKED) { + struct lov_stripe_md *lsm = llu_i2info(inode)->lli_smd; + fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK); + rc = llu_extent_unlock(fd, inode, lsm, LCK_GROUP, + &fd->fd_cwlockh); + } + + obdo.o_id = st->st_ino; obdo.o_valid = OBD_MD_FLID; valid = OBD_MD_FLTYPE | OBD_MD_FLMODE | OBD_MD_FLSIZE |OBD_MD_FLBLOCKS | OBD_MD_FLATIME | OBD_MD_FLMTIME | OBD_MD_FLCTIME; @@ -278,12 +313,12 @@ int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode) //ll_queue_done_writing(inode); rc = 0; } else if (rc) { - CERROR("inode %lu close failed: rc %d\n", lli->lli_st_ino, rc); + CERROR("inode %llu close failed: rc %d\n", st->st_ino, rc); } else { rc = llu_objects_destroy(req, inode); if (rc) - CERROR("inode %lu ll_objects destroy: rc = %d\n", - lli->lli_st_ino, rc); + CERROR("inode %llu ll_objects destroy: rc = %d\n", + st->st_ino, rc); } mdc_clear_open_replay_data(och); @@ -303,7 +338,7 @@ int llu_file_release(struct inode *inode) int rc = 0, rc2; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu\n", lli->lli_st_ino, + CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu\n", llu_i2stat(inode)->st_ino, lli->lli_st_generation); if (llu_is_root_inode(inode)) @@ -324,54 +359,34 @@ int llu_file_release(struct inode *inode) RETURN(rc); } +/* + * libsysio require us return 0 + */ int llu_iop_close(struct inode *inode) { int rc; + liblustre_wait_event(0); + rc = llu_file_release(inode); + if (rc) { + CERROR("file close error %d\n", rc); + } /* if open count == 0 && stale_flag is set, should we * remove the inode immediately? */ - return rc; + return 0; } -int llu_iop_ipreadv(struct inode *ino, - struct ioctx *ioctx) +_SYSIO_OFF_T llu_iop_pos(struct inode *ino, _SYSIO_OFF_T off) { ENTRY; - if (!ioctx->ioctx_iovlen) - RETURN(0); - if (ioctx->ioctx_iovlen < 0) - RETURN(-EINVAL); - - ioctx->ioctx_private = llu_file_read(ino, - ioctx->ioctx_iovec, - ioctx->ioctx_iovlen, - ioctx->ioctx_offset); - if (IS_ERR(ioctx->ioctx_private)) - return (PTR_ERR(ioctx->ioctx_private)); + liblustre_wait_event(0); - RETURN(0); -} - -int llu_iop_ipwritev(struct inode *ino, - struct ioctx *ioctx) -{ - ENTRY; - - if (!ioctx->ioctx_iovlen) - RETURN(0); - if (ioctx->ioctx_iovlen < 0) + if (off < 0 || off > ll_file_maxbytes(ino)) RETURN(-EINVAL); - ioctx->ioctx_private = llu_file_write(ino, - ioctx->ioctx_iovec, - ioctx->ioctx_iovlen, - ioctx->ioctx_offset); - if (IS_ERR(ioctx->ioctx_private)) - return (PTR_ERR(ioctx->ioctx_private)); - - RETURN(0); + RETURN(off); } /* this isn't where truncate starts. roughly: @@ -380,15 +395,17 @@ int llu_iop_ipwritev(struct inode *ino, static void llu_truncate(struct inode *inode) { struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct lov_stripe_md *lsm = lli->lli_smd; struct obdo oa = {0}; - int err; + int rc; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu\n", lli->lli_st_ino, - lli->lli_st_generation); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu(%p) to %llu\n", st->st_ino, + lli->lli_st_generation, inode, st->st_size); if (!lsm) { - CERROR("truncate on inode %lu with no objects\n", lli->lli_st_ino); + CDEBUG(D_INODE, "truncate on inode %llu with no objects\n", + st->st_ino); EXIT; return; } @@ -398,14 +415,16 @@ static void llu_truncate(struct inode *inode) obdo_from_inode(&oa, inode, OBD_MD_FLTYPE|OBD_MD_FLMODE|OBD_MD_FLATIME| OBD_MD_FLMTIME | OBD_MD_FLCTIME); + obd_adjust_kms(llu_i2obdexp(inode), lsm, st->st_size, 1); + CDEBUG(D_INFO, "calling punch for "LPX64" (all bytes after %Lu)\n", - oa.o_id, lli->lli_st_size); + oa.o_id, st->st_size); /* truncate == punch from new size to absolute end of file */ - err = obd_punch(llu_i2obdexp(inode), &oa, lsm, lli->lli_st_size, - OBD_OBJECT_EOF, NULL); - if (err) - CERROR("obd_truncate fails (%d) ino %lu\n", err, lli->lli_st_ino); + rc = obd_punch(llu_i2obdexp(inode), &oa, lsm, st->st_size, + OBD_OBJECT_EOF, NULL); + if (rc) + CERROR("obd_truncate fails (%d) ino %llu\n", rc, st->st_ino); else obdo_to_inode(inode, &oa, OBD_MD_FLSIZE | OBD_MD_FLBLOCKS | OBD_MD_FLATIME | OBD_MD_FLMTIME | @@ -413,13 +432,11 @@ static void llu_truncate(struct inode *inode) EXIT; return; -} +} /* llu_truncate */ int llu_vmtruncate(struct inode * inode, loff_t offset) { - struct llu_inode_info *lli = llu_i2info(inode); - - lli->lli_st_size = offset; + llu_i2stat(inode)->st_size = offset; llu_truncate(inode); diff --git a/lustre/liblustre/genlib.sh b/lustre/liblustre/genlib.sh index 4a3c356..f70116d 100755 --- a/lustre/liblustre/genlib.sh +++ b/lustre/liblustre/genlib.sh @@ -4,7 +4,10 @@ # # This script is to generate lib lustre library as a whole. It will leave # two files on current directory: liblustre.a and liblustre.so. -# Integrate them into Makefile.am later +# +# Most concern here is the libraries linking order +# +# FIXME: How to do this cleanly use makefile? # AR=/usr/bin/ar @@ -14,14 +17,16 @@ RANLIB=/usr/bin/ranlib CWD=`pwd` SYSIO=$1 +CRAY_PORTALS_LIBS=$2 +LIBS=$3 + +if [ ! -f $SYSIO/lib/libsysio.a ]; then + echo "ERROR: $SYSIO/lib/libsysio.a dosen't exist" + exit 1 +fi -#if [ ! -f $SYSIO/lib/libsysio.a ]; then -# echo "ERROR: $SYSIO/lib/libsysio.a dosen't exist" -# exit 1 -#fi -# # do cleanup at first -#rm -f liblustre.so +rm -f liblustre.so ALL_OBJS= @@ -35,16 +40,34 @@ build_obj_list() { # # special treatment for libsysio # -#sysio_tmp=$CWD/sysio_tmp_`date +%s` -#build_sysio_obj_list() { -# _objs=`$AR -t $1` -# mkdir -p $sysio_tmp -# $AR -x $1 -# mv $_objs $sysio_tmp -# for _lib in $_objs; do -# ALL_OBJS=$ALL_OBJS"$sysio_tmp/$_lib "; -# done -#} +sysio_tmp=$CWD/sysio_tmp_`date +%s` +rm -rf $sysio_tmp +build_sysio_obj_list() { + _objs=`$AR -t $1` + mkdir -p $sysio_tmp + cd $sysio_tmp + $AR -x $1 + cd .. + for _lib in $_objs; do + ALL_OBJS=$ALL_OBJS"$sysio_tmp/$_lib "; + done +} + +# +# special treatment for libportals.a +# +cray_tmp=$CWD/cray_tmp_`date +%s` +rm -rf $cray_tmp +build_cray_portals_obj_list() { + _objs=`$AR -t $1` + mkdir -p $cray_tmp + cd $cray_tmp + $AR -x $1 + cd .. + for _lib in $_objs; do + ALL_OBJS=$ALL_OBJS"$cray_tmp/$_lib "; + done +} # lustre components libs build_obj_list . libllite.a @@ -58,22 +81,24 @@ build_obj_list ../lvfs liblvfs.a # portals components libs build_obj_list ../../portals/utils libuptlctl.a -build_obj_list ../../portals/unals libtcpnal.a -build_obj_list ../../portals/portals libportals.a + +if [ "x$CRAY_PORTALS_LIBS" = "x" ]; then + build_obj_list ../../portals/unals libtcpnal.a + build_obj_list ../../portals/portals libportals.a +# if libportals is already in our LIBS we don't need to link against it here +elif $(echo "$LIBS" | grep -v -- "-lportals" >/dev/null) ; then + build_cray_portals_obj_list $CRAY_PORTALS_LIBS/libportals.a +fi # create static lib lsupport rm -f $CWD/liblsupport.a $AR -cru $CWD/liblsupport.a $ALL_OBJS $RANLIB $CWD/liblsupport.a -# libsysio components libs -build_obj_list $SYSIO/drivers/native libsysio_native.a -build_obj_list $SYSIO/drivers/sockets libsysio_sockets.a -build_obj_list $SYSIO/src libsysio.a -build_obj_list $SYSIO/dev/stdfd libsysio_stdfd.a -# -#build_sysio_obj_list $SYSIO/lib/libsysio.a -# +# if libsysio is already in our LIBS we don't need to link against it here +if $(echo "$LIBS" | grep -v -- "-lsysio" >/dev/null) ; then + build_sysio_obj_list $SYSIO/lib/libsysio.a +fi # create static lib lustre rm -f $CWD/liblustre.a @@ -83,6 +108,7 @@ $RANLIB $CWD/liblustre.a # create shared lib lustre rm -f $CWD/liblustre.so $LD -shared -o $CWD/liblustre.so -init __liblustre_setup_ -fini __liblustre_cleanup_ \ - $ALL_OBJS -lpthread + $ALL_OBJS -lcap -lpthread -#rm -rf $sysio_tmp +rm -rf $sysio_tmp +rm -rf $cray_tmp diff --git a/lustre/liblustre/llite_lib.c b/lustre/liblustre/llite_lib.c index f469710..9a69750 100644 --- a/lustre/liblustre/llite_lib.c +++ b/lustre/liblustre/llite_lib.c @@ -3,7 +3,7 @@ * * Lustre Light common routines * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -24,149 +24,43 @@ #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() || @@ -174,20 +68,12 @@ int lllib_init(char *dumpfile) osc_init()) return -1; - if (!g_zconf && parse_dump(dumpfile, lib_ioctl)) - return -1; - return _sysio_fssw_register("llite", &llu_fssw_ops); } - -#if 0 -static void llu_check_request() -{ - liblustre_wait_event(0); -} -#endif -int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov) +int liblustre_process_log(struct config_llog_instance *cfg, + char *mdsnid, char *mdsname, char *profile, + int allow_recov) { struct lustre_cfg_bufs bufs; struct lustre_cfg *lcfg; @@ -206,9 +92,9 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov) generate_random_uuid(uuid); class_uuid_unparse(uuid, &mdc_uuid); - nid = libcfs_str2nid(g_zconf_mdsnid); + nid = libcfs_str2nid(mdsnid); if (nid == PTL_NID_ANY) { - CERROR("Can't parse NID %s\n", g_zconf_mdsnid); + CERROR("Can't parse NID %s\n", mdsnid); RETURN(-EINVAL); } @@ -231,14 +117,14 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov) GOTO(out_del_uuid, err); lustre_cfg_bufs_reset(&bufs, name); - lustre_cfg_bufs_set_string(&bufs, 1, g_zconf_mdsname); + lustre_cfg_bufs_set_string(&bufs, 1, mdsname); lustre_cfg_bufs_set_string(&bufs, 2, peer); lcfg = lustre_cfg_new(LCFG_SETUP, &bufs); err = class_process_config(lcfg); lustre_cfg_free(lcfg); if (err < 0) GOTO(out_detach, err); - + obd = class_name2obd(name); if (obd == NULL) GOTO(out_cleanup, err = -EINVAL); @@ -251,14 +137,14 @@ int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov) err = obd_connect(&mdc_conn, obd, &mdc_uuid, NULL /*connect_flags*/); if (err) { CERROR("cannot connect to %s: rc = %d\n", - g_zconf_mdsname, err); + mdsname, err); GOTO(out_cleanup, err); } - + exp = class_conn2export(&mdc_conn); - + ctxt = exp->exp_obd->obd_llog_ctxt[LLOG_CONFIG_REPL_CTXT]; - rc = class_config_parse_llog(ctxt, g_zconf_profile, cfg); + rc = class_config_parse_llog(ctxt, profile, cfg); if (rc) { CERROR("class_config_parse_llog failed: rc = %d\n", rc); } @@ -290,13 +176,8 @@ out_del_uuid: out: if (rc == 0) rc = err; - - RETURN(rc); -} -static void sighandler_USR1(int signum) -{ - /* do nothing */ + RETURN(rc); } /* parse host:/mdsname/profile string */ @@ -312,7 +193,7 @@ int ll_parse_mount_target(const char *target, char **mdsnid, if ((s = strchr(buf, ':'))) { *mdsnid = buf; *s = '\0'; - + while (*++s == '/') ; *mdsname = s; @@ -326,119 +207,161 @@ int ll_parse_mount_target(const char *target, char **mdsnid, return -1; } -/* env variables */ -#define ENV_LUSTRE_MNTPNT "LIBLUSTRE_MOUNT_POINT" -#define ENV_LUSTRE_MNTTGT "LIBLUSTRE_MOUNT_TARGET" -#define ENV_LUSTRE_TIMEOUT "LIBLUSTRE_TIMEOUT" -#define ENV_LUSTRE_DUMPFILE "LIBLUSTRE_DUMPFILE" +/* + * early liblustre init + * called from C startup in catamount apps, before main() + * + * The following is a skeleton sysio startup sequence, + * as implemented in C startup (skipping error handling). + * In this framework none of these calls need be made here + * or in the apps themselves. The NAMESPACE_STRING specifying + * the initial set of fs ops (creates, mounts, etc.) is passed + * as an environment variable. + * + * _sysio_init(); + * _sysio_incore_init(); + * _sysio_native_init(); + * _sysio_lustre_init(); + * _sysio_boot(NAMESPACE_STRING); + * + * the name _sysio_lustre_init() follows the naming convention + * established in other fs drivers from libsysio: + * _sysio_incore_init(), _sysio_native_init() + * + * _sysio_lustre_init() must be called before _sysio_boot() + * to enable libsysio's processing of namespace init strings containing + * lustre filesystem operations + */ +int _sysio_lustre_init(void) +{ + int err; + char *timeout = NULL; + char *debug_mask = NULL; + char *debug_subsys = NULL; +#ifndef INIT_SYSIO + extern void __liblustre_cleanup_(void); +#endif -extern int _sysio_native_init(); +#if 0 + portal_debug = -1; + portal_subsystem_debug = -1; +#endif -extern unsigned int obd_timeout; + liblustre_init_random(); -/* global variables */ -int g_zconf = 0; /* zeroconf or dumpfile */ -char *g_zconf_mdsname = NULL; /* mdsname, for zeroconf */ -char *g_zconf_mdsnid = NULL; /* mdsnid, for zeroconf */ -char *g_zconf_profile = NULL; /* profile, for zeroconf */ + err = lllib_init(); + if (err) { + perror("init llite driver"); + return err; + } + timeout = getenv(ENV_LUSTRE_TIMEOUT); + if (timeout) { + obd_timeout = (unsigned int) strtol(timeout, NULL, 0); + printf("LibLustre: set obd timeout as %u seconds\n", + obd_timeout); + } + + /* debug masks */ + debug_mask = getenv(ENV_LUSTRE_DEBUG_MASK); + if (debug_mask) + portal_debug = (unsigned int) strtol(debug_mask, NULL, 0); + + debug_subsys = getenv(ENV_LUSTRE_DEBUG_SUBSYS); + if (debug_subsys) + portal_subsystem_debug = + (unsigned int) strtol(debug_subsys, NULL, 0); + +#ifndef INIT_SYSIO + (void)atexit(__liblustre_cleanup_); +#endif + return err; +} + +extern int _sysio_native_init(); +extern unsigned int obd_timeout; +char *lustre_path = NULL; void __liblustre_setup_(void) { - char *lustre_path = NULL; char *target = NULL; - char *timeout = NULL; - char *dumpfile = NULL; char *root_driver = "native"; char *lustre_driver = "llite"; char *root_path = "/"; unsigned mntflgs = 0; - int err; - /* consider tha case of starting multiple liblustre instances - * at a same time on single node. - */ - srand(time(NULL) + getpid()); - - signal(SIGUSR1, sighandler_USR1); - lustre_path = getenv(ENV_LUSTRE_MNTPNT); if (!lustre_path) { lustre_path = "/mnt/lustre"; } + /* mount target */ target = getenv(ENV_LUSTRE_MNTTGT); if (!target) { - dumpfile = getenv(ENV_LUSTRE_DUMPFILE); - if (!dumpfile) { - CERROR("Neither mount target, nor dumpfile\n"); - exit(1); - } - g_zconf = 0; - printf("LibLustre: mount point %s, dumpfile %s\n", - lustre_path, dumpfile); - } else { - if (ll_parse_mount_target(target, - &g_zconf_mdsnid, - &g_zconf_mdsname, - &g_zconf_profile)) { - CERROR("mal-formed target %s \n", target); - exit(1); - } - g_zconf = 1; - printf("LibLustre: mount point %s, target %s\n", - lustre_path, target); + printf("LibLustre: no mount target specified\n"); + exit(1); } + printf("LibLustre: mount point %s, target %s\n", + lustre_path, target); - timeout = getenv(ENV_LUSTRE_TIMEOUT); - if (timeout) { - obd_timeout = (unsigned int) atoi(timeout); - printf("LibLustre: set obd timeout as %u seconds\n", - obd_timeout); - } - if (_sysio_init() != 0) { +#ifdef INIT_SYSIO + /* initialize libsysio & mount rootfs */ + if (_sysio_init()) { perror("init sysio"); exit(1); } - - /* cygwin don't need native driver */ -#ifndef __CYGWIN__ _sysio_native_init(); -#endif err = _sysio_mount_root(root_path, root_driver, mntflgs, NULL); if (err) { - perror(root_driver); + fprintf(stderr, "sysio mount failed: %s\n", strerror(errno)); exit(1); } -#if 1 - portal_debug = 0; - portal_subsystem_debug = 0; -#endif - err = lllib_init(dumpfile); - if (err) { - perror("init llite driver"); + if (_sysio_lustre_init()) exit(1); - } +#endif /* INIT_SYSIO */ - err = mount("/", lustre_path, lustre_driver, mntflgs, NULL); + err = mount(target, lustre_path, lustre_driver, mntflgs, NULL); if (err) { - errno = -err; - perror(lustre_driver); + fprintf(stderr, "Lustre mount failed: %s\n", strerror(errno)); exit(1); } - -#if 0 - __sysio_hook_sys_enter = llu_check_request; - __sysio_hook_sys_leave = NULL; -#endif } void __liblustre_cleanup_(void) { +#ifndef INIT_SYSIO + /* guard against being called multiple times */ + static int cleaned = 0; + + if (cleaned) + return; + cleaned++; +#endif + + /* user app might chdir to a lustre directory, and leave busy pnode + * during finaly libsysio cleanup. here we chdir back to "/". + * but it can't fix the situation that liblustre is mounted + * at "/". + */ + chdir("/"); +#if 0 + umount(lustre_path); +#endif + /* we can't call umount here, because libsysio will not cleanup + * opening files for us. _sysio_shutdown() will cleanup fds at + * first but which will also close the sockets we need for umount + * liblutre. this delima lead to another hack in + * libsysio/src/file_hack.c FIXME + */ +#ifdef INIT_SYSIO _sysio_shutdown(); + cleanup_lib_portals(); PtlFini(); +#else + _sysio_shutdown(); +#endif } diff --git a/lustre/liblustre/llite_lib.h b/lustre/liblustre/llite_lib.h index 4462311..e254ea0 100644 --- a/lustre/liblustre/llite_lib.h +++ b/lustre/liblustre/llite_lib.h @@ -19,6 +19,8 @@ struct ll_file_data { struct obd_client_handle fd_mds_och; __u32 fd_flags; + struct lustre_handle fd_cwlockh; + unsigned long fd_gid; }; struct llu_sb_info @@ -36,7 +38,6 @@ struct llu_sb_info }; #define LL_SBI_NOLCK 0x1 -#define LL_SBI_READAHEAD 0x2 #define LLI_F_HAVE_OST_SIZE_LOCK 0 #define LLI_F_HAVE_MDS_SIZE_LOCK 1 @@ -49,15 +50,13 @@ struct llu_inode_info { char *lli_symlink_name; struct semaphore lli_open_sem; __u64 lli_maxbytes; - unsigned long lli_flags; + unsigned long lli_flags; /* for libsysio */ struct file_identifier lli_sysio_fid; struct lookup_intent *lli_it; - /* XXX workaround for libsysio unlink */ - int lli_stale_flag; /* XXX workaround for libsysio readdir */ loff_t lli_dir_pos; @@ -69,50 +68,9 @@ struct llu_inode_info { int lli_open_flags; int lli_open_count; - /* stat FIXME not 64 bit clean */ - dev_t lli_st_dev; - ino_t lli_st_ino; - mode_t lli_st_mode; - nlink_t lli_st_nlink; - uid_t lli_st_uid; - gid_t lli_st_gid; - dev_t lli_st_rdev; - loff_t lli_st_size; - unsigned int lli_st_blksize; - unsigned long lli_st_blocks; - time_t lli_st_atime; - time_t lli_st_mtime; - time_t lli_st_ctime; - /* not for stat, change it later */ - int lli_st_flags; - unsigned long lli_st_generation; -}; - -#define LLU_SYSIO_COOKIE_SIZE(exp, x) \ - (sizeof(struct llu_sysio_cookie) + \ - sizeof(struct ll_async_page) * (x) + \ - sizeof(struct page) * (x) + \ - llap_cookie_size * (x)) - -struct llu_sysio_cookie { - struct obd_io_group *lsc_oig; - struct inode *lsc_inode; - int lsc_maxpages; - int lsc_npages; - struct ll_async_page *lsc_llap; - struct page *lsc_pages; - void *lsc_llap_cookie; - __u64 lsc_rwcount; -}; - -/* XXX why uio.h haven't the definition? */ -#define MAX_IOVEC 32 - -struct llu_sysio_callback_args -{ - int ncookies; - struct llu_sysio_cookie *cookies[MAX_IOVEC]; + int lli_st_flags; + unsigned long lli_st_generation; }; static inline struct llu_sb_info *llu_fs2sbi(struct filesys *fs) @@ -125,6 +83,11 @@ static inline struct llu_inode_info *llu_i2info(struct inode *inode) return (struct llu_inode_info*)(inode->i_private); } +static inline struct intnl_stat *llu_i2stat(struct inode *inode) +{ + return &inode->i_stbuf; +} + static inline struct llu_sb_info *llu_i2sbi(struct inode *inode) { return llu_i2info(inode)->lli_sbi; @@ -153,8 +116,8 @@ do { \ OBD_ALLOC(temp, sizeof(*temp)); \ memcpy(temp, it, sizeof(*temp)); \ llu_i2info(inode)->lli_it = temp; \ - CDEBUG(D_DENTRY, "alloc intent %p to inode %p(ino %lu)\n", \ - temp, inode, llu_i2info(inode)->lli_st_ino); \ + CDEBUG(D_DENTRY, "alloc intent %p to inode %p(ino %llu)\n", \ + temp, inode, llu_i2stat(inode)->st_ino); \ } while(0) @@ -164,8 +127,8 @@ do { \ \ LASSERT(it); \ llu_i2info(inode)->lli_it = NULL; \ - CDEBUG(D_DENTRY, "dettach intent %p from inode %p(ino %lu)\n", \ - it, inode, llu_i2info(inode)->lli_st_ino); \ + CDEBUG(D_DENTRY, "dettach intent %p from inode %p(ino %llu)\n", \ + it, inode, llu_i2stat(inode)->st_ino); \ } while(0) /* interpet return codes from intent lookup */ @@ -186,26 +149,24 @@ struct it_cb_data { static inline void ll_i2uctxt(struct ll_uctxt *ctxt, struct inode *i1, struct inode *i2) { - struct llu_inode_info *lli1 = llu_i2info(i1); - struct llu_inode_info *lli2; + struct intnl_stat *st = llu_i2stat(i1); LASSERT(i1); LASSERT(ctxt); - if (in_group_p(lli1->lli_st_gid)) - ctxt->gid1 = lli1->lli_st_gid; + if (in_group_p(st->st_gid)) + ctxt->gid1 = st->st_gid; else ctxt->gid1 = -1; if (i2) { - lli2 = llu_i2info(i2); - if (in_group_p(lli2->lli_st_gid)) - ctxt->gid2 = lli2->lli_st_gid; + st = llu_i2stat(i2); + if (in_group_p(st->st_gid)) + ctxt->gid2 = st->st_gid; else ctxt->gid2 = -1; - } else { - ctxt->gid2 = -1; - } + } else + ctxt->gid2 = 0; } @@ -215,12 +176,6 @@ typedef int (*intent_finish_cb)(struct ptlrpc_request *, int llu_intent_lock(struct inode *parent, struct pnode *pnode, struct lookup_intent *, int flags, intent_finish_cb); -/* FIXME */ -static inline int ll_permission(struct inode *inode, int flag, void * unused) -{ - return 0; -} - static inline __u64 ll_file_maxbytes(struct inode *inode) { return llu_i2info(inode)->lli_maxbytes; @@ -232,16 +187,19 @@ struct mount_option_s char *osc_uuid; }; +#define IS_BAD_PTR(ptr) \ + ((unsigned long)(ptr) == 0 || (unsigned long)(ptr) > -1000UL) + /* llite_lib.c */ void generate_random_uuid(unsigned char uuid_out[16]); -int liblustre_process_log(struct config_llog_instance *cfg, int allow_recov); +int liblustre_process_log(struct config_llog_instance *cfg, + char *mdsnid, + char *mdsname, + char *profile, + int allow_recov); int ll_parse_mount_target(const char *target, char **mdsnid, char **mdsname, char **profile); -extern int g_zconf; -extern char *g_zconf_mdsnid; -extern char *g_zconf_mdsname; -extern char *g_zconf_profile; extern struct mount_option_s mount_option; /* super.c */ @@ -252,6 +210,7 @@ void obdo_from_inode(struct obdo *dst, struct inode *src, obd_flag valid); int ll_it_open_error(int phase, struct lookup_intent *it); struct inode *llu_iget(struct filesys *fs, struct lustre_md *md); int llu_inode_getattr(struct inode *inode, struct lov_stripe_md *lsm); +int llu_setattr_raw(struct inode *inode, struct iattr *attr); extern struct fssw_ops llu_fssw_ops; @@ -266,20 +225,15 @@ int llu_create(struct inode *dir, struct pnode_base *pnode, int mode); int llu_iop_open(struct pnode *pnode, int flags, mode_t mode); int llu_mdc_close(struct obd_export *mdc_exp, struct inode *inode); int llu_iop_close(struct inode *inode); -int llu_iop_ipreadv(struct inode *ino, struct ioctx *ioctxp); -int llu_iop_ipwritev(struct inode *ino, struct ioctx *ioctxp); +_SYSIO_OFF_T llu_iop_pos(struct inode *ino, _SYSIO_OFF_T off); int llu_vmtruncate(struct inode * inode, loff_t offset); void obdo_refresh_inode(struct inode *dst, struct obdo *src, obd_flag valid); int llu_objects_destroy(struct ptlrpc_request *request, struct inode *dir); /* rw.c */ -int llu_iop_iodone(struct ioctx *ioctxp __IS_UNUSED); -struct llu_sysio_callback_args* -llu_file_write(struct inode *inode, const struct iovec *iovec, - size_t iovlen, loff_t pos); -struct llu_sysio_callback_args* -llu_file_read(struct inode *inode, const struct iovec *iovec, - size_t iovlen, loff_t pos); +int llu_iop_read(struct inode *ino, struct ioctx *ioctxp); +int llu_iop_write(struct inode *ino, struct ioctx *ioctxp); +int llu_iop_iodone(struct ioctx *ioctxp); int llu_glimpse_size(struct inode *inode); int llu_extent_lock(struct ll_file_data *fd, struct inode *inode, struct lov_stripe_md *lsm, int mode, diff --git a/lustre/liblustre/lutil.c b/lustre/liblustre/lutil.c index 7ad8aa5..2227b0b 100644 --- a/lustre/liblustre/lutil.c +++ b/lustre/liblustre/lutil.c @@ -25,37 +25,37 @@ #include <signal.h> #include <sys/types.h> -#ifndef REDSTORM #include <fcntl.h> +#ifdef HAVE_NETDB_H #include <netdb.h> +#endif #include <syscall.h> #include <sys/utsname.h> +#ifdef HAVE_NETINET_IN_H #include <netinet/in.h> +#endif #include <sys/socket.h> +#ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> -#else -#include <sys/socket.h> +#endif +#ifdef HAVE_CATAMOUNT_DATA_H #include <catamount/data.h> #endif #include "lutil.h" -#ifdef CRAY_PORTALS +#if CRAY_PORTALS void portals_debug_dumplog(void){}; #endif -unsigned int portal_subsystem_debug = ~0 - S_NAL; +unsigned int portal_subsystem_debug = ~0 - (S_PORTALS | S_NAL); unsigned int portal_debug = 0; struct task_struct *current; -ptl_handle_ni_t tcpnal_ni; -ptl_nid_t tcpnal_mynid; void *inter_module_get(char *arg) { - if (!strcmp(arg, "tcpnal_ni")) - return &tcpnal_ni; - else if (!strcmp(arg, "ldlm_cli_cancel_unused")) + if (!strcmp(arg, "ldlm_cli_cancel_unused")) return ldlm_cli_cancel_unused; else if (!strcmp(arg, "ldlm_namespace_cleanup")) return ldlm_namespace_cleanup; @@ -65,68 +65,14 @@ void *inter_module_get(char *arg) return NULL; } -char *portals_nid2str(int nal, ptl_nid_t nid, char *str) -{ - if (nid == PTL_NID_ANY) { - snprintf(str, PTL_NALFMT_SIZE - 1, "%s", - "PTL_NID_ANY"); - return str; - } - - switch(nal){ -#ifndef CRAY_PORTALS - case TCPNAL: - /* userspace NAL */ - case OPENIBNAL: - case SOCKNAL: - snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u.%u.%u.%u", - (__u32)(nid >> 32), HIPQUAD(nid)); - break; - case QSWNAL: - case GMNAL: - snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u", - (__u32)(nid >> 32), (__u32)nid); - break; -#endif - default: - snprintf(str, PTL_NALFMT_SIZE - 1, "?%d? %llx", - nal, (long long)nid); - break; - } - return str; -} - -char *portals_id2str(int nal, ptl_process_id_t id, char *str) -{ - switch(nal){ -#ifndef CRAY_PORTALS - case TCPNAL: - /* userspace NAL */ - case OPENIBNAL: - case SOCKNAL: - snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u.%u.%u.%u,%u", - (__u32)(id.nid >> 32), HIPQUAD((id.nid)) , id.pid); - break; - case QSWNAL: - case GMNAL: - snprintf(str, PTL_NALFMT_SIZE - 1, "%u:%u,%u", - (__u32)(id.nid >> 32), (__u32)id.nid, id.pid); - break; -#endif - default: - snprintf(str, PTL_NALFMT_SIZE - 1, "?%d? %llx,%lx", - nal, (long long)id.nid, (long)id.pid ); - break; - } - return str; -} - -#ifndef REDSTORM /* * random number generator stuff */ +#ifdef LIBLUSTRE_USE_URANDOM static int _rand_dev_fd = -1; +#endif +#ifdef HAVE_GETHOSTBYNAME static int get_ipv4_addr() { struct utsname myname; @@ -148,25 +94,33 @@ static int get_ipv4_addr() return ip; } +#endif void liblustre_init_random() { int seed; struct timeval tv; +#ifdef LIBLUSTRE_USE_URANDOM _rand_dev_fd = syscall(SYS_open, "/dev/urandom", O_RDONLY); if (_rand_dev_fd >= 0) { - if (syscall(SYS_read, _rand_dev_fd, &seed, sizeof(int)) == - sizeof(int)) { + if (syscall(SYS_read, _rand_dev_fd, + &seed, sizeof(int)) == sizeof(int)) { srand(seed); return; } syscall(SYS_close, _rand_dev_fd); _rand_dev_fd = -1; } +#endif /* LIBLUSTRE_USE_URANDOM */ +#ifdef HAVE_GETHOSTBYNAME + seed = get_ipv4_addr(); +#else + seed = _my_pnid; +#endif gettimeofday(&tv, NULL); - srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(get_ipv4_addr())); + srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(seed)); } void get_random_bytes(void *buf, int size) @@ -174,12 +128,14 @@ void get_random_bytes(void *buf, int size) char *p = buf; LASSERT(size >= 0); +#ifdef LIBLUSTRE_USE_URANDOM if (_rand_dev_fd >= 0) { if (syscall(SYS_read, _rand_dev_fd, buf, size) == size) return; syscall(SYS_close, _rand_dev_fd); _rand_dev_fd = -1; } +#endif while (size--) *p++ = rand(); @@ -187,6 +143,7 @@ void get_random_bytes(void *buf, int size) static void init_capability(int *res) { +#ifdef HAVE_LIBCAP cap_t syscap; cap_flag_value_t capval; int i; @@ -207,69 +164,20 @@ static void init_capability(int *res) } } } +#else + /* + * set fake cap flags to ship to linux server + * from client platforms that have none (eg. catamount) + * full capability for root + * no capability for anybody else + */ +#define FAKE_ROOT_CAP 0x1ffffeff +#define FAKE_USER_CAP 0 + + *res = (current->fsuid == 0) ? FAKE_ROOT_CAP: FAKE_USER_CAP; +#endif } -void liblustre_set_nal_nid() -{ - pid_t pid; - uint32_t ip; - struct in_addr in; - - /* need to setup mynid before tcpnal initialization */ - /* a meaningful nid could help debugging */ - ip = get_ipv4_addr(); - if (ip == 0) - get_random_bytes(&ip, sizeof(ip)); - pid = getpid() & 0xffffffff; - tcpnal_mynid = ((uint64_t)ip << 32) | pid; - - in.s_addr = htonl(ip); - printf("LibLustre: TCPNAL NID: %016llx (%s:%u)\n", - tcpnal_mynid, inet_ntoa(in), pid); -} - -#else /* REDSTORM */ - -void liblustre_init_random() -{ - struct timeval tv; - UINT32 nodeid; - - gettimeofday(&tv, NULL); - nodeid = _my_pnid; - srand(tv.tv_sec + tv.tv_usec + getpid() + __swab32(nodeid)); -} - -void get_random_bytes(void *buf, int size) -{ - char *p = buf; - LASSERT(size >= 0); - - while (size--) - *p++ = rand(); -} - -static void init_capability(int *res) -{ - *res = 0; -} - -void liblustre_set_nal_nid() -{ - pid_t pid; - uint32_t ip; - - ip = _my_pnid; - if (ip & 0xFF) - ip <<= 8; - pid = getpid() & 0xFF; - tcpnal_mynid = ip | pid; - printf("LibLustre: NAL NID: %08x (%u)\n", - tcpnal_mynid, pid); -} - -#endif /* REDSOTRM */ - int in_group_p(gid_t gid) { int i; @@ -292,9 +200,6 @@ int liblustre_init_current(char *comm) CERROR("Not enough memory\n"); return -ENOMEM; } - current->fs = ¤t->__fs; - current->fs->umask = umask(0777); - umask(current->fs->umask); strncpy(current->comm, comm, sizeof(current->comm)); current->pid = getpid(); @@ -343,10 +248,3 @@ void cleanup_lib_portals() { ptlrpc_exit_portals(); } - -int -libcfs_nal_cmd(struct portals_cfg *pcfg) -{ - /* handle portals command if we want */ - return 0; -} diff --git a/lustre/liblustre/lutil.h b/lustre/liblustre/lutil.h index dc67a23..dc5e6e2 100644 --- a/lustre/liblustre/lutil.h +++ b/lustre/liblustre/lutil.h @@ -28,7 +28,6 @@ void liblustre_init_random(void); int liblustre_init_current(char *comm); -void liblustre_set_nal_nid(void); int init_lib_portals(void); void cleanup_lib_portals(void); diff --git a/lustre/liblustre/namei.c b/lustre/liblustre/namei.c index 74eddb2..9e69a9e 100644 --- a/lustre/liblustre/namei.c +++ b/lustre/liblustre/namei.c @@ -3,7 +3,7 @@ * * Lustre Light name resolution * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -32,11 +32,16 @@ #include <sys/fcntl.h> #include <sys/queue.h> +#ifdef HAVE_XTIO_H +#include <xtio.h> +#endif #include <sysio.h> #include <fs.h> #include <mount.h> #include <inode.h> +#ifdef HAVE_FILE_H #include <file.h> +#endif #undef LIST_HEAD @@ -70,6 +75,7 @@ static void ll_intent_release(struct lookup_intent *it) EXIT; } +#if 0 /* * remove the stale inode from pnode */ @@ -94,6 +100,7 @@ void unhook_stale_inode(struct pnode *pno) EXIT; return; } +#endif void llu_lookup_finish_locks(struct lookup_intent *it, struct pnode *pnode) { @@ -102,14 +109,14 @@ void llu_lookup_finish_locks(struct lookup_intent *it, struct pnode *pnode) if (it && pnode->p_base->pb_ino != NULL) { struct inode *inode = pnode->p_base->pb_ino; - CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%lu)\n", - inode, llu_i2info(inode)->lli_st_ino, + CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%llu/%lu)\n", + inode, llu_i2stat(inode)->st_ino, llu_i2info(inode)->lli_st_generation); mdc_set_lock_data(&it->d.lustre.it_lock_handle, inode); } - /* drop IT_LOOKUP locks */ - if (it->it_op == IT_LOOKUP) + /* drop lookup/getattr locks */ + if (it->it_op == IT_LOOKUP || it->it_op == IT_GETATTR) ll_intent_release(it); } @@ -140,23 +147,25 @@ int llu_mdc_blocking_ast(struct ldlm_lock *lock, case LDLM_CB_CANCELING: { struct inode *inode = llu_inode_from_lock(lock); struct llu_inode_info *lli; + struct intnl_stat *st; /* Invalidate all dentries associated with this inode */ if (inode == NULL) break; lli = llu_i2info(inode); + st = llu_i2stat(inode); clear_bit(LLI_F_HAVE_MDS_SIZE_LOCK, &lli->lli_flags); - if (lock->l_resource->lr_name.name[0] != lli->lli_st_ino || + if (lock->l_resource->lr_name.name[0] != st->st_ino || lock->l_resource->lr_name.name[1] != lli->lli_st_generation) { - LDLM_ERROR(lock, "data mismatch with ino %lu/%lu", - lli->lli_st_ino, lli->lli_st_generation); + LDLM_ERROR(lock, "data mismatch with ino %llu/%lu", + st->st_ino, lli->lli_st_generation); } - if (S_ISDIR(lli->lli_st_mode)) { - CDEBUG(D_INODE, "invalidating inode %lu\n", - lli->lli_st_ino); + if (S_ISDIR(st->st_mode)) { + CDEBUG(D_INODE, "invalidating inode %llu\n", + st->st_ino); llu_invalidate_inode_pages(inode); } @@ -215,8 +224,8 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it) int rc; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:name=%s,intent=%x\n", - pb->pb_name.name, it ? it->it_op : 0); + CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,intent=%x\n", + (int)pb->pb_name.len, pb->pb_name.name, it ? it->it_op : 0); /* We don't want to cache negative dentries, so return 0 immediately. * We believe that this is safe, that negative dentries cannot be @@ -226,26 +235,16 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it) RETURN(0); } - /* check stale inode */ - if (llu_i2info(pb->pb_ino)->lli_stale_flag) - unhook_stale_inode(pnode); - - /* check again because unhook_stale_inode() might generate - * negative pnode */ - if (pb->pb_ino == NULL) { - CDEBUG(D_INODE, "negative pb\n"); - RETURN(0); - } - /* This is due to bad interaction with libsysio. remove this when we * switched to libbsdio XXX */ { struct llu_inode_info *lli = llu_i2info(pb->pb_ino); + struct intnl_stat *st = llu_i2stat(pb->pb_ino); if (lli->lli_it) { - CDEBUG(D_INODE, "inode %lu still have intent " + CDEBUG(D_INODE, "inode %llu still have intent " "%p(opc 0x%x), release it\n", - lli->lli_st_ino, lli->lli_it, + st->st_ino, lli->lli_it, lli->lli_it->it_op); ll_intent_release(lli->lli_it); OBD_FREE(lli->lli_it, sizeof(*lli->lli_it)); @@ -279,14 +278,19 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it) GOTO(out, rc = 0); rc = pnode_revalidate_finish(req, 1, it, pnode); + if (rc != 0) { + ll_intent_release(it); + GOTO(out, rc = 0); + } + rc = 1; /* Note: ll_intent_lock may cause a callback, check this! */ - if (it->it_op & (IT_OPEN | IT_GETATTR)) + if (it->it_op & IT_OPEN) LL_SAVE_INTENT(pb->pb_ino, it); - RETURN(1); + out: - if (req) + if (req && rc == 1) ptlrpc_req_finished(req); if (rc == 0) { LASSERT(pb->pb_ino); @@ -294,9 +298,6 @@ int llu_pb_revalidate(struct pnode *pnode, int flags, struct lookup_intent *it) pb->pb_ino = NULL; } else { llu_lookup_finish_locks(it, pnode); - llu_i2info(pb->pb_ino)->lli_stale_flag = 0; - if (it->it_op & (IT_OPEN | IT_GETATTR)) - LL_SAVE_INTENT(pb->pb_ino, it); } RETURN(rc); } @@ -311,13 +312,37 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset, struct inode *inode = NULL; int rc; + /* libsysio require us generate inode right away if success. + * so if mds created new inode for us we need make sure it + * succeeded. thus for any error we can't delay to the + * llu_file_open() time. */ + if (it_disposition(it, DISP_OPEN_CREATE) && + it_open_error(DISP_OPEN_CREATE, it)) { + CDEBUG(D_INODE, "detect mds create error\n"); + return it_open_error(DISP_OPEN_CREATE, it); + } + if (it_disposition(it, DISP_OPEN_OPEN) && + it_open_error(DISP_OPEN_OPEN, it)) { + CDEBUG(D_INODE, "detect mds open error\n"); + /* undo which did by mdc_intent_lock */ + if (it_disposition(it, DISP_OPEN_CREATE) && + !it_open_error(DISP_OPEN_CREATE, it)) { + LASSERT(request); + LASSERT(atomic_read(&request->rq_refcount) > 1); + CDEBUG(D_INODE, "dec a ref of req %p\n", request); + ptlrpc_req_finished(request); + } + return it_open_error(DISP_OPEN_OPEN, it); + } + /* NB 1 request reference will be taken away by ll_intent_lock() * when I return - * Note: libsysio require the inode must be generated here */ - if ((it->it_op & IT_CREAT) || !it_disposition(it, DISP_LOOKUP_NEG)) { + if (!it_disposition(it, DISP_LOOKUP_NEG) || + (it->it_op & IT_CREAT)) { struct lustre_md md; struct llu_inode_info *lli; + struct intnl_stat *st; ENTRY; rc = mdc_req2lustre_md(request, offset, sbi->ll_osc_exp, &md); @@ -325,23 +350,23 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset, RETURN(rc); inode = llu_iget(parent->i_fs, &md); - if (!inode) { + if (!inode || IS_ERR(inode)) { /* free the lsm if we allocated one above */ if (md.lsm != NULL) obd_free_memmd(sbi->ll_osc_exp, &md.lsm); - RETURN(-ENOMEM); + RETURN(inode ? PTR_ERR(inode) : -ENOMEM); } else if (md.lsm != NULL && llu_i2info(inode)->lli_smd != md.lsm) { obd_free_memmd(sbi->ll_osc_exp, &md.lsm); } lli = llu_i2info(inode); + st = llu_i2stat(inode); /* If this is a stat, get the authoritative file size */ - if (it->it_op == IT_GETATTR && S_ISREG(lli->lli_st_mode) && + if (it->it_op == IT_GETATTR && S_ISREG(st->st_mode) && lli->lli_smd != NULL) { struct lov_stripe_md *lsm = lli->lli_smd; - struct ost_lvb lvb; ldlm_error_t rc; LASSERT(lsm->lsm_object_id != 0); @@ -360,7 +385,7 @@ static int lookup_it_finish(struct ptlrpc_request *request, int offset, } /* intent will be further used in cases of open()/getattr() */ - if (inode && (it->it_op & (IT_OPEN | IT_GETATTR))) + if (inode && (it->it_op & IT_OPEN)) LL_SAVE_INTENT(inode, it); child->p_base->pb_ino = inode; @@ -517,6 +542,8 @@ int llu_iop_lookup(struct pnode *pnode, int rc; ENTRY; + liblustre_wait_event(0); + *inop = NULL; /* the mount root inode have no name, so don't call @@ -550,4 +577,3 @@ int llu_iop_lookup(struct pnode *pnode, RETURN(rc); } - diff --git a/lustre/liblustre/rw.c b/lustre/liblustre/rw.c index ea99362..a20e18c 100644 --- a/lustre/liblustre/rw.c +++ b/lustre/liblustre/rw.c @@ -3,7 +3,7 @@ * * Lustre Light block IO * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -28,22 +28,63 @@ #include <assert.h> #include <time.h> #include <sys/types.h> +#include <sys/stat.h> #include <sys/queue.h> #include <fcntl.h> +#include <sys/uio.h> +#ifdef HAVE_XTIO_H +#include <xtio.h> +#endif #include <sysio.h> #include <fs.h> #include <mount.h> #include <inode.h> +#ifdef HAVE_FILE_H #include <file.h> +#endif #undef LIST_HEAD #include "llite_lib.h" +struct llu_io_group +{ + struct obd_io_group *lig_oig; + struct inode *lig_inode; + int lig_maxpages; + int lig_npages; + __u64 lig_rwcount; + struct ll_async_page *lig_llaps; + struct page *lig_pages; + void *lig_llap_cookies; +}; + +#define LLU_IO_GROUP_SIZE(x) \ + (sizeof(struct llu_io_group) + \ + (sizeof(struct ll_async_page) + \ + sizeof(struct page) + \ + llap_cookie_size) * (x)) + +struct llu_io_session +{ + struct inode *lis_inode; + int lis_cmd; + int lis_max_groups; + int lis_ngroups; + struct llu_io_group *lis_groups[0]; +}; +#define LLU_IO_SESSION_SIZE(x) \ + (sizeof(struct llu_io_session) + (x) * 2 * sizeof(void *)) + + +typedef ssize_t llu_file_piov_t(const struct iovec *iovec, int iovlen, + _SYSIO_OFF_T pos, ssize_t len, + void *private); + size_t llap_cookie_size; -static int llu_lock_to_stripe_offset(struct inode *inode,struct ldlm_lock *lock) +static int llu_lock_to_stripe_offset(struct inode *inode, struct ldlm_lock *lock) { struct llu_inode_info *lli = llu_i2info(inode); struct lov_stripe_md *lsm = lli->lli_smd; @@ -162,7 +203,7 @@ static int llu_glimpse_callback(struct ldlm_lock *lock, void *reqp) lvb->lvb_size = lli->lli_smd->lsm_oinfo[stripe].loi_kms; LDLM_DEBUG(lock, "i_size: %llu -> stripe number %u -> kms "LPU64, - lli->lli_st_size, stripe, lvb->lvb_size); + llu_i2stat(inode)->st_size, stripe, lvb->lvb_size); iput: I_RELE(inode); out: @@ -175,37 +216,35 @@ static int llu_glimpse_callback(struct ldlm_lock *lock, void *reqp) return rc; } -__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms); -__u64 lov_merge_blocks(struct lov_stripe_md *lsm); -__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time); - /* NB: lov_merge_size will prefer locally cached writes if they extend the * file (because it prefers KMS over RSS when larger) */ int llu_glimpse_size(struct inode *inode) { struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct llu_sb_info *sbi = llu_i2sbi(inode); ldlm_policy_data_t policy = { .l_extent = { 0, OBD_OBJECT_EOF } }; struct lustre_handle lockh = { 0 }; int rc, flags = LDLM_FL_HAS_INTENT; ENTRY; - CDEBUG(D_DLMTRACE, "Glimpsing inode %lu\n", lli->lli_st_ino); + CDEBUG(D_DLMTRACE, "Glimpsing inode %llu\n", st->st_ino); rc = obd_enqueue(sbi->ll_osc_exp, lli->lli_smd, LDLM_EXTENT, &policy, LCK_PR, &flags, llu_extent_lock_callback, ldlm_completion_ast, llu_glimpse_callback, inode, sizeof(struct ost_lvb), lustre_swab_ost_lvb, &lockh); - if (rc > 0) - RETURN(-EIO); - - lli->lli_st_size = lov_merge_size(lli->lli_smd, 0); - lli->lli_st_blocks = lov_merge_blocks(lli->lli_smd); - lli->lli_st_mtime = lov_merge_mtime(lli->lli_smd, lli->lli_st_mtime); + if (rc) { + CERROR("obd_enqueue returned rc %d, returning -EIO\n", rc); + RETURN(rc > 0 ? -EIO : rc); + } - CDEBUG(D_DLMTRACE, "glimpse: size: %llu, blocks: %lu\n", - lli->lli_st_size, lli->lli_st_blocks); + st->st_size = lov_merge_size(lli->lli_smd, 0); + st->st_blocks = lov_merge_blocks(lli->lli_smd); + //lli->lli_st_mtime = lov_merge_mtime(lli->lli_smd, inode->i_mtime); + CDEBUG(D_DLMTRACE, "glimpse: size: %llu, blocks: %llu\n", + st->st_size, st->st_blocks); obd_cancel(sbi->ll_osc_exp, lli->lli_smd, LCK_PR, &lockh); @@ -218,7 +257,7 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode, int ast_flags) { struct llu_sb_info *sbi = llu_i2sbi(inode); - struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); int rc; ENTRY; @@ -229,8 +268,8 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode, (sbi->ll_flags & LL_SBI_NOLCK)) RETURN(0); - CDEBUG(D_DLMTRACE, "Locking inode %lu, start "LPU64" end "LPU64"\n", - lli->lli_st_ino, policy->l_extent.start, policy->l_extent.end); + CDEBUG(D_DLMTRACE, "Locking inode %llu, start "LPU64" end "LPU64"\n", + st->st_ino, policy->l_extent.start, policy->l_extent.end); rc = obd_enqueue(sbi->ll_osc_exp, lsm, LDLM_EXTENT, policy, mode, &ast_flags, llu_extent_lock_callback, @@ -241,91 +280,13 @@ int llu_extent_lock(struct ll_file_data *fd, struct inode *inode, if (policy->l_extent.start == 0 && policy->l_extent.end == OBD_OBJECT_EOF) - lli->lli_st_size = lov_merge_size(lsm, 1); - - RETURN(rc); -} - -#if 0 -int llu_extent_lock_no_validate(struct ll_file_data *fd, - struct inode *inode, - struct lov_stripe_md *lsm, - int mode, - struct ldlm_extent *extent, - struct lustre_handle *lockh, - int ast_flags) -{ - struct llu_sb_info *sbi = llu_i2sbi(inode); - struct llu_inode_info *lli = llu_i2info(inode); - int rc; - ENTRY; - - LASSERT(lockh->cookie == 0); - - /* XXX phil: can we do this? won't it screw the file size up? */ - if ((fd && (fd->fd_flags & LL_FILE_IGNORE_LOCK)) || - (sbi->ll_flags & LL_SBI_NOLCK)) - RETURN(0); - - CDEBUG(D_DLMTRACE, "Locking inode %lu, start "LPU64" end "LPU64"\n", - lli->lli_st_ino, extent->start, extent->end); + st->st_size = lov_merge_size(lsm, 1); - rc = obd_enqueue(sbi->ll_osc_exp, lsm, NULL, LDLM_EXTENT, extent, - sizeof(extent), mode, &ast_flags, - llu_extent_lock_callback, inode, lockh); + //inode->i_mtime = lov_merge_mtime(lsm, inode->i_mtime); RETURN(rc); } -/* - * this grabs a lock and manually implements behaviour that makes it look like - * the OST is returning the file size with each lock acquisition. - */ -int llu_extent_lock(struct ll_file_data *fd, struct inode *inode, - struct lov_stripe_md *lsm, int mode, - struct ldlm_extent *extent, struct lustre_handle *lockh) -{ - struct llu_inode_info *lli = llu_i2info(inode); - struct obd_export *exp = llu_i2obdexp(inode); - struct ldlm_extent size_lock; - struct lustre_handle match_lockh = {0}; - int flags, rc, matched; - ENTRY; - - rc = llu_extent_lock_no_validate(fd, inode, lsm, mode, extent, lockh, 0); - if (rc != ELDLM_OK) - RETURN(rc); - - if (test_bit(LLI_F_HAVE_OST_SIZE_LOCK, &lli->lli_flags)) - RETURN(0); - - rc = llu_inode_getattr(inode, lsm); - if (rc) { - llu_extent_unlock(fd, inode, lsm, mode, lockh); - RETURN(rc); - } - - size_lock.start = lli->lli_st_size; - size_lock.end = OBD_OBJECT_EOF; - - /* XXX I bet we should be checking the lock ignore flags.. */ - /* FIXME use LDLM_FL_TEST_LOCK instead */ - flags = LDLM_FL_CBPENDING | LDLM_FL_BLOCK_GRANTED; - matched = obd_match(exp, lsm, LDLM_EXTENT, &size_lock, - sizeof(size_lock), LCK_PR, &flags, inode, - &match_lockh); - - /* hey, alright, we hold a size lock that covers the size we - * just found, its not going to change for a while.. */ - if (matched == 1) { - set_bit(LLI_F_HAVE_OST_SIZE_LOCK, &lli->lli_flags); - obd_cancel(exp, lsm, LCK_PR, &match_lockh); - } - - RETURN(0); -} -#endif - int llu_extent_unlock(struct ll_file_data *fd, struct inode *inode, struct lov_stripe_md *lsm, int mode, struct lustre_handle *lockh) @@ -354,14 +315,6 @@ struct ll_async_page { struct inode *llap_inode; }; -static struct ll_async_page *llap_from_cookie(void *cookie) -{ - struct ll_async_page *llap = cookie; - if (llap->llap_magic != LLAP_MAGIC) - return ERR_PTR(-EINVAL); - return llap; -}; - static void llu_ap_fill_obdo(void *data, int cmd, struct obdo *oa) { struct ll_async_page *llap; @@ -370,12 +323,7 @@ static void llu_ap_fill_obdo(void *data, int cmd, struct obdo *oa) obd_flag valid_flags; ENTRY; - llap = llap_from_cookie(data); - if (IS_ERR(llap)) { - EXIT; - return; - } - + llap = LLAP_FROM_COOKIE(data); inode = llap->llap_inode; lsm = llu_i2info(inode)->lli_smd; @@ -394,13 +342,9 @@ static void llu_ap_completion(void *data, int cmd, struct obdo *oa, int rc) { struct ll_async_page *llap; struct page *page; + ENTRY; - llap = llap_from_cookie(data); - if (IS_ERR(llap)) { - EXIT; - return; - } - + llap = LLAP_FROM_COOKIE(data); llap->llap_queued = 0; page = llap->llap_page; @@ -412,164 +356,43 @@ static void llu_ap_completion(void *data, int cmd, struct obdo *oa, int rc) EXIT; } +static void llu_ap_get_ucred(void *data, struct lvfs_ucred *luc) +{ + struct ll_async_page *llap; + struct ll_uctxt ctxt; + ENTRY; + + llap = LLAP_FROM_COOKIE(data); + + luc->luc_fsuid = current->fsuid; + luc->luc_fsgid = current->fsgid; + luc->luc_cap = current->cap_effective; + ll_i2uctxt(&ctxt, llap->llap_inode, NULL); + luc->luc_suppgid1 = ctxt.gid1; + + EXIT; +} + static struct obd_async_page_ops llu_async_page_ops = { .ap_make_ready = NULL, .ap_refresh_count = NULL, .ap_fill_obdo = llu_ap_fill_obdo, .ap_completion = llu_ap_completion, + .ap_get_ucred = llu_ap_get_ucred, }; -static -struct llu_sysio_cookie* get_sysio_cookie(struct inode *inode, - struct obd_export *exp, int maxpages) -{ - struct llu_sysio_cookie *cookie; - int rc; - - if (!llap_cookie_size) - llap_cookie_size = obd_prep_async_page(llu_i2obdexp(inode), - NULL, NULL, NULL, 0, - NULL, NULL, NULL); - OBD_ALLOC(cookie, LLU_SYSIO_COOKIE_SIZE(exp, maxpages)); - if (cookie == NULL) - goto out; - - I_REF(inode); - cookie->lsc_inode = inode; - cookie->lsc_maxpages = maxpages; - cookie->lsc_llap = (struct ll_async_page *)(cookie + 1); - cookie->lsc_pages = (struct page *) (cookie->lsc_llap + maxpages); - cookie->lsc_llap_cookie = (void *)(cookie->lsc_pages + maxpages); - - rc = oig_init(&cookie->lsc_oig); - if (rc) { - OBD_FREE(cookie, LLU_SYSIO_COOKIE_SIZE(exp, maxpages)); - cookie = NULL; - } - -out: - return cookie; -} - -static -void put_sysio_cookie(struct llu_sysio_cookie *cookie) -{ - struct lov_stripe_md *lsm = llu_i2info(cookie->lsc_inode)->lli_smd; - struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode); - struct ll_async_page *llap = cookie->lsc_llap; -#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE - struct page *pages = cookie->lsc_pages; -#endif - int i; - - for (i = 0; i< cookie->lsc_maxpages; i++) { - if (llap[i].llap_cookie) - obd_teardown_async_page(exp, lsm, NULL, - llap[i].llap_cookie); -#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE - if (pages[i]._managed) { - free(pages[i].addr); - pages[i]._managed = 0; - } -#endif - } - - I_RELE(cookie->lsc_inode); - - oig_release(cookie->lsc_oig); - OBD_FREE(cookie, LLU_SYSIO_COOKIE_SIZE(exp, cookie->lsc_maxpages)); -} - -#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE -/* Note: these code should be removed finally, don't need - * more cleanup - */ -static -int prepare_unaligned_write(struct llu_sysio_cookie *cookie) -{ - struct inode *inode = cookie->lsc_inode; - struct llu_inode_info *lli = llu_i2info(inode); - struct lov_stripe_md *lsm = lli->lli_smd; - struct obdo oa; - struct page *pages = cookie->lsc_pages; - int i, pgidx[2] = {0, cookie->lsc_npages-1}; - int rc; - ENTRY; - - for (i = 0; i < 2; i++) { - struct page *oldpage = &pages[pgidx[i]]; - struct page newpage; - struct brw_page pg; - char *newbuf; - - if (i == 0 && pgidx[0] == pgidx[1]) - continue; - - LASSERT(oldpage->_offset + oldpage->_count <= PAGE_CACHE_SIZE); - - if (oldpage->_count == PAGE_CACHE_SIZE) - continue; - - if (oldpage->index << PAGE_CACHE_SHIFT >= - lli->lli_st_size) - continue; - - newbuf = malloc(PAGE_CACHE_SIZE); - if (!newbuf) - return -ENOMEM; - - newpage.index = oldpage->index; - newpage.addr = newbuf; - - pg.pg = &newpage; - pg.off = ((obd_off)newpage.index << PAGE_CACHE_SHIFT); - if (pg.off + PAGE_CACHE_SIZE > lli->lli_st_size) - pg.count = lli->lli_st_size % PAGE_CACHE_SIZE; - else - pg.count = PAGE_CACHE_SIZE; - pg.flag = 0; - - oa.o_id = lsm->lsm_object_id; - oa.o_mode = lli->lli_st_mode; - oa.o_valid = OBD_MD_FLID | OBD_MD_FLMODE | OBD_MD_FLTYPE; - - /* issue read */ - rc = obd_brw(OBD_BRW_READ, llu_i2obdexp(inode), &oa, lsm, 1, &pg, NULL); - if (rc) { - free(newbuf); - RETURN(rc); - } - - /* copy page content, and reset page params */ - memcpy(newbuf + oldpage->_offset, - (char*)oldpage->addr + oldpage->_offset, - oldpage->_count); - - oldpage->addr = newbuf; - if ((((obd_off)oldpage->index << PAGE_CACHE_SHIFT) + - oldpage->_offset + oldpage->_count) > lli->lli_st_size) - oldpage->_count += oldpage->_offset; - else - oldpage->_count = PAGE_CACHE_SIZE; - oldpage->_offset = 0; - oldpage->_managed = 1; - } - - RETURN(0); -} -#endif - -static -int llu_prep_async_io(struct llu_sysio_cookie *cookie, int cmd, - char *buf, loff_t pos, size_t count) +static int llu_queue_pio(int cmd, struct llu_io_group *group, + char *buf, size_t count, loff_t pos) { - struct llu_inode_info *lli = llu_i2info(cookie->lsc_inode); + struct llu_inode_info *lli = llu_i2info(group->lig_inode); + struct intnl_stat *st = llu_i2stat(group->lig_inode); struct lov_stripe_md *lsm = lli->lli_smd; - struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode); - struct page *pages = cookie->lsc_pages; - struct ll_async_page *llap = cookie->lsc_llap; - void *llap_cookie = cookie->lsc_llap_cookie; - int i, rc, npages = 0; + struct obd_export *exp = llu_i2obdexp(group->lig_inode); + struct page *pages = &group->lig_pages[group->lig_npages],*page = pages; + struct ll_async_page *llap = &group->lig_llaps[group->lig_npages]; + void *llap_cookie = group->lig_llap_cookies + + llap_cookie_size * group->lig_npages; + int i, rc, npages = 0, ret_bytes = 0; ENTRY; if (!exp) @@ -587,350 +410,393 @@ int llu_prep_async_io(struct llu_sysio_cookie *cookie, int cmd, /* prevent read beyond file range */ if ((cmd == OBD_BRW_READ) && - (pos + bytes) >= lli->lli_st_size) { - if (pos >= lli->lli_st_size) + (pos + bytes) >= st->st_size) { + if (pos >= st->st_size) break; - bytes = lli->lli_st_size - pos; + bytes = st->st_size - pos; } /* prepare page for this index */ - pages[npages].index = index; - pages[npages].addr = buf - offset; + page->index = index; + page->addr = buf - offset; - pages[npages]._offset = offset; - pages[npages]._count = bytes; + page->_offset = offset; + page->_count = bytes; + page++; npages++; count -= bytes; pos += bytes; buf += bytes; - cookie->lsc_rwcount += bytes; + group->lig_rwcount += bytes; + ret_bytes += bytes; } while (count); - cookie->lsc_npages = npages; + group->lig_npages += npages; -#ifdef LIBLUSTRE_HANDLE_UNALIGNED_PAGE - if (cmd == OBD_BRW_WRITE) { - rc = prepare_unaligned_write(cookie); - if (rc) - RETURN(rc); - } -#endif - - for (i = 0; i < npages; i++) { - llap[i].llap_magic = LLAP_MAGIC; - llap[i].llap_cookie = llap_cookie + i * llap_cookie_size; - rc = obd_prep_async_page(exp, lsm, NULL, &pages[i], - (obd_off)pages[i].index << PAGE_SHIFT, + for (i = 0, page = pages; i < npages; + i++, page++, llap++, llap_cookie += llap_cookie_size){ + llap->llap_magic = LLAP_MAGIC; + llap->llap_cookie = llap_cookie; + rc = obd_prep_async_page(exp, lsm, NULL, page, + (obd_off)page->index << PAGE_SHIFT, &llu_async_page_ops, - &llap[i], &llap[i].llap_cookie); + llap, &llap->llap_cookie); if (rc) { - llap[i].llap_cookie = NULL; + LASSERT(rc < 0); + llap->llap_cookie = NULL; RETURN(rc); } - CDEBUG(D_CACHE, "llap %p page %p cookie %p obj off "LPU64"\n", - &llap[i], &pages[i], llap[i].llap_cookie, - (obd_off)pages[i].index << PAGE_SHIFT); - pages[i].private = (unsigned long)&llap[i]; - llap[i].llap_page = &pages[i]; - llap[i].llap_inode = cookie->lsc_inode; - - rc = obd_queue_group_io(exp, lsm, NULL, cookie->lsc_oig, - llap[i].llap_cookie, cmd, - pages[i]._offset, pages[i]._count, 0, + CDEBUG(D_CACHE, "llap %p page %p group %p obj off "LPU64"\n", + llap, page, llap->llap_cookie, + (obd_off)pages->index << PAGE_SHIFT); + page->private = (unsigned long)llap; + llap->llap_page = page; + llap->llap_inode = group->lig_inode; + + rc = obd_queue_group_io(exp, lsm, NULL, group->lig_oig, + llap->llap_cookie, cmd, + page->_offset, page->_count, 0, ASYNC_READY | ASYNC_URGENT | ASYNC_COUNT_STABLE | ASYNC_GROUP_SYNC); - if (rc) + if (rc) { + LASSERT(rc < 0); RETURN(rc); + } - llap[i].llap_queued = 1; + llap->llap_queued = 1; } - RETURN(0); + RETURN(ret_bytes); } static -int llu_start_async_io(struct llu_sysio_cookie *cookie) +struct llu_io_group * get_io_group(struct inode *inode, int maxpages) { - struct lov_stripe_md *lsm = llu_i2info(cookie->lsc_inode)->lli_smd; - struct obd_export *exp = llu_i2obdexp(cookie->lsc_inode); + struct llu_io_group *group; + int rc; - return obd_trigger_group_io(exp, lsm, NULL, cookie->lsc_oig); -} + if (!llap_cookie_size) + llap_cookie_size = obd_prep_async_page(llu_i2obdexp(inode), + NULL, NULL, NULL, 0, + NULL, NULL, NULL); -/* - * read/write a continuous buffer for an inode (zero-copy) - */ -struct llu_sysio_cookie* -llu_rw(int cmd, struct inode *inode, char *buf, size_t count, loff_t pos) -{ - struct llu_sysio_cookie *cookie; - int max_pages, rc; - ENTRY; + OBD_ALLOC(group, LLU_IO_GROUP_SIZE(maxpages)); + if (!group) + return ERR_PTR(-ENOMEM); - max_pages = (count >> PAGE_SHIFT) + 2; + I_REF(inode); + group->lig_inode = inode; + group->lig_maxpages = maxpages; + group->lig_llaps = (struct ll_async_page *)(group + 1); + group->lig_pages = (struct page *)(&group->lig_llaps[maxpages]); + group->lig_llap_cookies = (void *)(&group->lig_pages[maxpages]); - cookie = get_sysio_cookie(inode, llu_i2obdexp(inode), max_pages); - if (!cookie) - RETURN(ERR_PTR(-ENOMEM)); + rc = oig_init(&group->lig_oig); + if (rc) { + OBD_FREE(group, LLU_IO_GROUP_SIZE(maxpages)); + return ERR_PTR(rc); + } - rc = llu_prep_async_io(cookie, cmd, buf, pos, count); - if (rc) - GOTO(out_cleanup, rc); + return group; +} - rc = llu_start_async_io(cookie); - if (rc) - GOTO(out_cleanup, rc); +static int max_io_pages(ssize_t len, int iovlen) +{ + return (((len + PAGE_SIZE -1) / PAGE_SIZE) + 2 + iovlen - 1); +} -/* - rc = oig_wait(&oig); - if (rc) { - CERROR("file i/o error!\n"); - rw_count = rc; +static +void put_io_group(struct llu_io_group *group) +{ + struct lov_stripe_md *lsm = llu_i2info(group->lig_inode)->lli_smd; + struct obd_export *exp = llu_i2obdexp(group->lig_inode); + struct ll_async_page *llap = group->lig_llaps; + int i; + + for (i = 0; i < group->lig_npages; i++, llap++) { + if (llap->llap_cookie) + obd_teardown_async_page(exp, lsm, NULL, + llap->llap_cookie); } -*/ - RETURN(cookie); -out_cleanup: - put_sysio_cookie(cookie); - RETURN(ERR_PTR(rc)); + I_RELE(group->lig_inode); + + oig_release(group->lig_oig); + OBD_FREE(group, LLU_IO_GROUP_SIZE(group->lig_maxpages)); } -struct llu_sysio_callback_args* -llu_file_write(struct inode *inode, const struct iovec *iovec, - size_t iovlen, loff_t pos) +static +ssize_t llu_file_prwv(const struct iovec *iovec, int iovlen, + _SYSIO_OFF_T pos, ssize_t len, + void *private) { + struct llu_io_session *session = (struct llu_io_session *) private; + struct inode *inode = session->lis_inode; struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct ll_file_data *fd = lli->lli_file_data; struct lustre_handle lockh = {0}; struct lov_stripe_md *lsm = lli->lli_smd; struct obd_export *exp = NULL; ldlm_policy_data_t policy; - struct llu_sysio_callback_args *lsca; - struct llu_sysio_cookie *cookie; - ldlm_error_t err; - int iovidx; + struct llu_io_group *iogroup; + int astflag = (lli->lli_open_flags & O_NONBLOCK) ? + LDLM_FL_BLOCK_NOWAIT : 0; + __u64 kms; + int err, is_read, lock_mode, iovidx, ret; ENTRY; - /* XXX consider other types later */ - if (!S_ISREG(lli->lli_st_mode)) - LBUG(); - - LASSERT(iovlen <= MAX_IOVEC); + /* in a large iov read/write we'll be repeatedly called. + * so give a chance to answer cancel ast here + */ + liblustre_wait_event(0); exp = llu_i2obdexp(inode); if (exp == NULL) - RETURN(ERR_PTR(-EINVAL)); + RETURN(-EINVAL); + + if (len == 0 || iovlen == 0) + RETURN(0); + + if (pos + len > lli->lli_maxbytes) + RETURN(-ERANGE); - OBD_ALLOC(lsca, sizeof(*lsca)); - if (!lsca) - RETURN(ERR_PTR(-ENOMEM)); + iogroup = get_io_group(inode, max_io_pages(len, iovlen)); + if (IS_ERR(iogroup)) + RETURN(PTR_ERR(iogroup)); + + is_read = session->lis_cmd == OBD_BRW_READ; + lock_mode = is_read ? LCK_PR : LCK_PW; + + if (!is_read && (lli->lli_open_flags & O_APPEND)) { + policy.l_extent.start = 0; + policy.l_extent.end = OBD_OBJECT_EOF; + } else { + policy.l_extent.start = pos; + policy.l_extent.end = pos + len - 1; + } + + err = llu_extent_lock(fd, inode, lsm, lock_mode, &policy, + &lockh, astflag); + if (err != ELDLM_OK) + GOTO(err_put, err); + + if (is_read) { + kms = lov_merge_size(lsm, 1); + if (policy.l_extent.end > kms) { + /* A glimpse is necessary to determine whether we + * return a short read or some zeroes at the end of + * the buffer */ + if ((err = llu_glimpse_size(inode))) { + llu_extent_unlock(fd, inode, lsm, + lock_mode, &lockh); + GOTO(err_put, err); + } + } else { + st->st_size = kms; + } + } else { + if (lli->lli_open_flags & O_APPEND) + pos = st->st_size; + } - /* FIXME optimize the following extent locking */ for (iovidx = 0; iovidx < iovlen; iovidx++) { - char *buf = (char*)iovec[iovidx].iov_base; + char *buf = (char *) iovec[iovidx].iov_base; size_t count = iovec[iovidx].iov_len; - if (count == 0) + if (!count) continue; + if (len < count) + count = len; + if (IS_BAD_PTR(buf) || IS_BAD_PTR(buf + count)) { + llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh); + GOTO(err_put, err = -EFAULT); + } - if (pos + count > lli->lli_maxbytes) - GOTO(err_out, err = -ERANGE); - - /* FIXME libsysio haven't handle O_APPEND?? */ - policy.l_extent.start = pos; - policy.l_extent.end = pos + count - 1; - - err = llu_extent_lock(fd, inode, lsm, LCK_PW, &policy, - &lockh, 0); - if (err != ELDLM_OK) - GOTO(err_out, err = -ENOLCK); - - CDEBUG(D_INFO, "Writing inode %lu, "LPSZ" bytes, offset %Lu\n", - lli->lli_st_ino, count, pos); - - cookie = llu_rw(OBD_BRW_WRITE, inode, buf, count, pos); - if (!IS_ERR(cookie)) { - /* save cookie */ - lsca->cookies[lsca->ncookies++] = cookie; - pos += count; - obd_adjust_kms(exp, lsm, pos, 0); - /* file size grow */ - if (pos > lli->lli_st_size) - lli->lli_st_size = pos; + if (is_read) { + if (pos >= st->st_size) + break; } else { - llu_extent_unlock(fd, inode, lsm, LCK_PW, &lockh); - GOTO(err_out, err = PTR_ERR(cookie)); + if (pos >= lli->lli_maxbytes) { + llu_extent_unlock(fd, inode, lsm, lock_mode, + &lockh); + GOTO(err_put, err = -EFBIG); + } + if (pos + count >= lli->lli_maxbytes) + count = lli->lli_maxbytes - pos; } - /* XXX errors? */ - err = llu_extent_unlock(fd, inode, lsm, LCK_PW, &lockh); - if (err) - CERROR("extent unlock error %d\n", err); + ret = llu_queue_pio(session->lis_cmd, iogroup, buf, count, pos); + if (ret < 0) { + llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh); + GOTO(err_put, err = ret); + } else { + pos += ret; + if (!is_read) { + LASSERT(ret == count); + obd_adjust_kms(exp, lsm, pos, 0); + /* file size grow immediately */ + if (pos > st->st_size) + st->st_size = pos; + } + len -= ret; + if (!len) + break; + } } + LASSERT(len == 0 || is_read); /* libsysio should guarantee this */ - RETURN(lsca); + err = llu_extent_unlock(fd, inode, lsm, lock_mode, &lockh); + if (err) + CERROR("extent unlock error %d\n", err); -err_out: - /* teardown all async stuff */ - while (lsca->ncookies--) { - put_sysio_cookie(lsca->cookies[lsca->ncookies]); - } - OBD_FREE(lsca, sizeof(*lsca)); + err = obd_trigger_group_io(exp, lsm, NULL, iogroup->lig_oig); + if (err) + GOTO(err_put, err); - RETURN(ERR_PTR(err)); + session->lis_groups[session->lis_ngroups++] = iogroup; + RETURN(0); +err_put: + put_io_group(iogroup); + RETURN((ssize_t)err); } -#if 0 -static void llu_update_atime(struct inode *inode) +static +struct llu_io_session *get_io_session(struct inode *ino, int ngroups, int cmd) { - struct llu_inode_info *lli = llu_i2info(inode); - -#ifdef USE_ATIME - struct iattr attr; - - attr.ia_atime = LTIME_S(CURRENT_TIME); - attr.ia_valid = ATTR_ATIME; + struct llu_io_session *session; - if (lli->lli_st_atime == attr.ia_atime) return; - if (IS_RDONLY(inode)) return; - if (IS_NOATIME(inode)) return; + OBD_ALLOC(session, LLU_IO_SESSION_SIZE(ngroups)); + if (!session) + return NULL; - /* ll_inode_setattr() sets inode->i_atime from attr.ia_atime */ - llu_inode_setattr(inode, &attr, 0); -#else - /* update atime, but don't explicitly write it out just this change */ - inode->i_atime = CURRENT_TIME; -#endif + I_REF(ino); + session->lis_inode = ino; + session->lis_max_groups = ngroups; + session->lis_cmd = cmd; + return session; } -#endif -struct llu_sysio_callback_args* -llu_file_read(struct inode *inode, const struct iovec *iovec, - size_t iovlen, loff_t pos) +static void put_io_session(struct llu_io_session *session) { - struct llu_inode_info *lli = llu_i2info(inode); - struct ll_file_data *fd = lli->lli_file_data; - struct lov_stripe_md *lsm = lli->lli_smd; - struct lustre_handle lockh = { 0 }; - ldlm_policy_data_t policy; - struct llu_sysio_callback_args *lsca; - struct llu_sysio_cookie *cookie; - __u64 kms; - int iovidx; - - ldlm_error_t err; - ENTRY; + int i; - OBD_ALLOC(lsca, sizeof(*lsca)); - if (!lsca) - RETURN(ERR_PTR(-ENOMEM)); + for (i = 0; i < session->lis_ngroups; i++) { + if (session->lis_groups[i]) { + put_io_group(session->lis_groups[i]); + session->lis_groups[i] = NULL; + } + } - for (iovidx = 0; iovidx < iovlen; iovidx++) { - char *buf = iovec[iovidx].iov_base; - size_t count = iovec[iovidx].iov_len; + I_RELE(session->lis_inode); + OBD_FREE(session, LLU_IO_SESSION_SIZE(session->lis_max_groups)); +} - /* "If nbyte is 0, read() will return 0 and have no other results." - * -- Single Unix Spec */ - if (count == 0) - continue; +static int llu_file_rwx(struct inode *ino, + struct ioctx *ioctx, + int read) +{ + struct llu_io_session *session; + ssize_t cc; + int cmd = read ? OBD_BRW_READ : OBD_BRW_WRITE; + ENTRY; - policy.l_extent.start = pos; - policy.l_extent.end = pos + count - 1; + LASSERT(ioctx->ioctx_xtvlen >= 0); + LASSERT(ioctx->ioctx_iovlen >= 0); - err = llu_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh, 0); - if (err != ELDLM_OK) - GOTO(err_out, err = -ENOLCK); + liblustre_wait_event(0); - kms = lov_merge_size(lsm, 1); - if (policy.l_extent.end > kms) { - /* A glimpse is necessary to determine whether we - * return a short read or some zeroes at the end of - * the buffer */ - if (llu_glimpse_size(inode)) { - llu_extent_unlock(fd, inode, lsm,LCK_PR,&lockh); - GOTO(err_out, err = -ENOLCK); - } - } else { - lli->lli_st_size = kms; - } + if (!ioctx->ioctx_xtvlen) + RETURN(0); - CDEBUG(D_INFO, "Reading inode %lu, "LPSZ" bytes, offset %Ld, " - "i_size "LPU64"\n", lli->lli_st_ino, count, pos, - lli->lli_st_size); + /* XXX consider other types later */ + if (S_ISDIR(llu_i2stat(ino)->st_mode)) + RETURN(-EISDIR); + if (!S_ISREG(llu_i2stat(ino)->st_mode)) + RETURN(-EOPNOTSUPP); + + session = get_io_session(ino, ioctx->ioctx_xtvlen * 2, cmd); + if (!session) + RETURN(-ENOMEM); + + cc = _sysio_enumerate_extents(ioctx->ioctx_xtv, ioctx->ioctx_xtvlen, + ioctx->ioctx_iov, ioctx->ioctx_iovlen, + llu_file_prwv, session); + + if (cc >= 0) { + LASSERT(!ioctx->ioctx_cc); + ioctx->ioctx_private = session; + RETURN(0); + } else { + put_io_session(session); + RETURN(cc); + } +} - if (pos >= lli->lli_st_size) { - llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh); - break; - } +int llu_iop_read(struct inode *ino, + struct ioctx *ioctx) +{ + return llu_file_rwx(ino, ioctx, 1); +} - cookie = llu_rw(OBD_BRW_READ, inode, buf, count, pos); - if (!IS_ERR(cookie)) { - /* save cookie */ - lsca->cookies[lsca->ncookies++] = cookie; - pos += count; - } else { - llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh); - GOTO(err_out, err = PTR_ERR(cookie)); - } +int llu_iop_write(struct inode *ino, + struct ioctx *ioctx) +{ + struct iattr iattr; + int rc; - /* XXX errors? */ - err = llu_extent_unlock(fd, inode, lsm, LCK_PR, &lockh); - if (err) - CERROR("extent_unlock fail: %d\n", err); - } -#if 0 - if (readed > 0) - llu_update_atime(inode); -#endif - RETURN(lsca); + memset(&iattr, 0, sizeof(iattr)); + iattr.ia_mtime = iattr.ia_atime = CURRENT_TIME; + iattr.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_RAW; -err_out: - /* teardown all async stuff */ - while (lsca->ncookies--) { - put_sysio_cookie(lsca->cookies[lsca->ncookies]); + liblustre_wait_event(0); + rc = llu_setattr_raw(ino, &iattr); + if (rc) { + CERROR("failed to set mtime/atime during write: %d", rc); + /* XXX should continue or return error? */ } - OBD_FREE(lsca, sizeof(*lsca)); - RETURN(ERR_PTR(err)); + return llu_file_rwx(ino, ioctx, 0); } -int llu_iop_iodone(struct ioctx *ioctxp) +int llu_iop_iodone(struct ioctx *ioctx) { - struct llu_sysio_callback_args *lsca = ioctxp->ioctx_private; - struct llu_sysio_cookie *cookie; + struct llu_io_session *session; + struct llu_io_group *group; int i, err = 0, rc = 0; ENTRY; - /* write/read(fd, buf, 0) */ - if (!lsca) { - ioctxp->ioctx_cc = 0; - RETURN(1); - } + liblustre_wait_event(0); - LASSERT(!IS_ERR(lsca)); + session = (struct llu_io_session *) ioctx->ioctx_private; + LASSERT(session); + LASSERT(!IS_ERR(session)); - for (i = 0; i < lsca->ncookies; i++) { - cookie = lsca->cookies[i]; - if (cookie) { - err = oig_wait(cookie->lsc_oig); - if (err && !rc) - rc = err; + for (i = 0; i < session->lis_ngroups; i++) { + group = session->lis_groups[i]; + if (group) { + if (!rc) { + err = oig_wait(group->lig_oig); + if (err) + rc = err; + } if (!rc) - ioctxp->ioctx_cc += cookie->lsc_rwcount; - put_sysio_cookie(cookie); + ioctx->ioctx_cc += group->lig_rwcount; + put_io_group(group); + session->lis_groups[i] = NULL; } } if (rc) { LASSERT(rc < 0); - ioctxp->ioctx_cc = -1; - ioctxp->ioctx_errno = -rc; + ioctx->ioctx_cc = -1; + ioctx->ioctx_errno = -rc; } - OBD_FREE(lsca, sizeof(*lsca)); - ioctxp->ioctx_private = NULL; + put_io_session(session); + ioctx->ioctx_private = NULL; RETURN(1); } diff --git a/lustre/liblustre/super.c b/lustre/liblustre/super.c index 9972f1a..5dcd3b8 100644 --- a/lustre/liblustre/super.c +++ b/lustre/liblustre/super.c @@ -3,7 +3,7 @@ * * Lustre Light Super operations * - * Copyright (c) 2002, 2003 Cluster File Systems, Inc. + * Copyright (c) 2002-2004 Cluster File Systems, Inc. * * This file is part of Lustre, http://www.lustre.org. * @@ -37,37 +37,86 @@ # include <sys/statfs.h> #endif +#ifdef HAVE_XTIO_H +#include <xtio.h> +#endif #include <sysio.h> #include <fs.h> #include <mount.h> #include <inode.h> +#ifdef HAVE_FILE_H #include <file.h> +#endif #undef LIST_HEAD #include "llite_lib.h" +#ifndef MAY_EXEC +#define MAY_EXEC 1 +#define MAY_WRITE 2 +#define MAY_READ 4 +#endif + +#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH) + +static int ll_permission(struct inode *inode, int mask) +{ + struct intnl_stat *st = llu_i2stat(inode); + mode_t mode = st->st_mode; + + if (current->fsuid == st->st_uid) + mode >>= 6; + else if (in_group_p(st->st_gid)) + mode >>= 3; + + if ((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask) + return 0; + + if ((mask & (MAY_READ|MAY_WRITE)) || + (st->st_mode & S_IXUGO)) + if (capable(CAP_DAC_OVERRIDE)) + return 0; + + if (mask == MAY_READ || + (S_ISDIR(st->st_mode) && !(mask & MAY_WRITE))) { + if (capable(CAP_DAC_READ_SEARCH)) + return 0; + } + + return -EACCES; +} + static void llu_fsop_gone(struct filesys *fs) { struct llu_sb_info *sbi = (struct llu_sb_info *) fs->fs_private; struct obd_device *obd = class_exp2obd(sbi->ll_mdc_exp); - struct ll_fid rootfid; + int next = 0; ENTRY; list_del(&sbi->ll_conn_chain); obd_disconnect(sbi->ll_osc_exp); - - /* NULL request to force sync on the MDS, and get the last_committed - * value to flush remaining RPCs from the sending queue on client. - * - * XXX This should be an mdc_sync() call to sync the whole MDS fs, - * which we can call for other reasons as well. - */ - if (!obd->obd_no_recov) - mdc_getstatus(sbi->ll_mdc_exp, &rootfid); - obd_disconnect(sbi->ll_mdc_exp); + while ((obd = class_devices_in_group(&sbi->ll_sb_uuid, &next)) != NULL){ + struct lustre_cfg_bufs bufs; + struct lustre_cfg *lcfg; + int err; + + lustre_cfg_bufs_reset(&bufs, obd->obd_name); + lcfg = lustre_cfg_new(LCFG_CLEANUP, &bufs); + err = class_process_config(lcfg); + lustre_cfg_free(lcfg); + if (err) + CERROR("cleanup failed: %s\n", obd->obd_name); + + lustre_cfg_bufs_reset(&bufs, obd->obd_name); + lcfg = lustre_cfg_new(LCFG_DETACH, &bufs); + err = class_process_config(lcfg); + if (err) + CERROR("detach failed: %s\n", obd->obd_name); + } + OBD_FREE(sbi, sizeof(*sbi)); EXIT; @@ -79,6 +128,7 @@ void llu_update_inode(struct inode *inode, struct mds_body *body, struct lov_stripe_md *lsm) { struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); LASSERT ((lsm != NULL) == ((body->valid & OBD_MD_FLEASIZE) != 0)); if (lsm != NULL) { @@ -89,41 +139,45 @@ void llu_update_inode(struct inode *inode, struct mds_body *body, lli->lli_maxbytes = PAGE_CACHE_MAXBYTES; } else { if (memcmp(lli->lli_smd, lsm, sizeof(*lsm))) { - CERROR("lsm mismatch for inode %ld\n", - lli->lli_st_ino); + CERROR("lsm mismatch for inode %lld\n", + st->st_ino); LBUG(); } } } if (body->valid & OBD_MD_FLID) - lli->lli_st_ino = body->ino; + st->st_ino = body->ino; if (body->valid & OBD_MD_FLATIME) - LTIME_S(lli->lli_st_atime) = body->atime; + LTIME_S(st->st_atime) = body->atime; if (body->valid & OBD_MD_FLMTIME) - LTIME_S(lli->lli_st_mtime) = body->mtime; + LTIME_S(st->st_mtime) = body->mtime; if (body->valid & OBD_MD_FLCTIME) - LTIME_S(lli->lli_st_ctime) = body->ctime; + LTIME_S(st->st_ctime) = body->ctime; if (body->valid & OBD_MD_FLMODE) - lli->lli_st_mode = (lli->lli_st_mode & S_IFMT)|(body->mode & ~S_IFMT); + st->st_mode = (st->st_mode & S_IFMT)|(body->mode & ~S_IFMT); if (body->valid & OBD_MD_FLTYPE) - lli->lli_st_mode = (lli->lli_st_mode & ~S_IFMT)|(body->mode & S_IFMT); + st->st_mode = (st->st_mode & ~S_IFMT)|(body->mode & S_IFMT); + if (S_ISREG(st->st_mode)) + st->st_blksize = min(2UL * PTLRPC_MAX_BRW_SIZE, LL_MAX_BLKSIZE); + else + st->st_blksize = 4096; if (body->valid & OBD_MD_FLUID) - lli->lli_st_uid = body->uid; + st->st_uid = body->uid; if (body->valid & OBD_MD_FLGID) - lli->lli_st_gid = body->gid; - if (body->valid & OBD_MD_FLFLAGS) - lli->lli_st_flags = body->flags; + st->st_gid = body->gid; if (body->valid & OBD_MD_FLNLINK) - lli->lli_st_nlink = body->nlink; - if (body->valid & OBD_MD_FLGENER) - lli->lli_st_generation = body->generation; + st->st_nlink = body->nlink; if (body->valid & OBD_MD_FLRDEV) - lli->lli_st_rdev = body->rdev; + st->st_rdev = body->rdev; if (body->valid & OBD_MD_FLSIZE) - lli->lli_st_size = body->size; + st->st_size = body->size; if (body->valid & OBD_MD_FLBLOCKS) - lli->lli_st_blocks = body->blocks; + st->st_blocks = body->blocks; + if (body->valid & OBD_MD_FLFLAGS) + lli->lli_st_flags = body->flags; + if (body->valid & OBD_MD_FLGENER) + lli->lli_st_generation = body->generation; /* fillin fid */ if (body->valid & OBD_MD_FLID) @@ -137,35 +191,36 @@ void llu_update_inode(struct inode *inode, struct mds_body *body, void obdo_to_inode(struct inode *dst, struct obdo *src, obd_flag valid) { struct llu_inode_info *lli = llu_i2info(dst); + struct intnl_stat *st = llu_i2stat(dst); valid &= src->o_valid; if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME)) - CDEBUG(D_INODE, "valid %x, cur time %lu/%lu, new %lu/%lu\n", - src->o_valid, - LTIME_S(lli->lli_st_mtime), LTIME_S(lli->lli_st_ctime), + CDEBUG(D_INODE,"valid "LPX64", cur time %lu/%lu, new %lu/%lu\n", + src->o_valid, + LTIME_S(st->st_mtime), LTIME_S(st->st_ctime), (long)src->o_mtime, (long)src->o_ctime); if (valid & OBD_MD_FLATIME) - LTIME_S(lli->lli_st_atime) = src->o_atime; + LTIME_S(st->st_atime) = src->o_atime; if (valid & OBD_MD_FLMTIME) - LTIME_S(lli->lli_st_mtime) = src->o_mtime; - if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(lli->lli_st_ctime)) - LTIME_S(lli->lli_st_ctime) = src->o_ctime; + LTIME_S(st->st_mtime) = src->o_mtime; + if (valid & OBD_MD_FLCTIME && src->o_ctime > LTIME_S(st->st_ctime)) + LTIME_S(st->st_ctime) = src->o_ctime; if (valid & OBD_MD_FLSIZE) - lli->lli_st_size = src->o_size; + st->st_size = src->o_size; if (valid & OBD_MD_FLBLOCKS) /* allocation of space */ - lli->lli_st_blocks = src->o_blocks; + st->st_blocks = src->o_blocks; if (valid & OBD_MD_FLBLKSZ) - lli->lli_st_blksize = src->o_blksize; + st->st_blksize = src->o_blksize; if (valid & OBD_MD_FLTYPE) - lli->lli_st_mode = (lli->lli_st_mode & ~S_IFMT) | (src->o_mode & S_IFMT); + st->st_mode = (st->st_mode & ~S_IFMT) | (src->o_mode & S_IFMT); if (valid & OBD_MD_FLMODE) - lli->lli_st_mode = (lli->lli_st_mode & S_IFMT) | (src->o_mode & ~S_IFMT); + st->st_mode = (st->st_mode & S_IFMT) | (src->o_mode & ~S_IFMT); if (valid & OBD_MD_FLUID) - lli->lli_st_uid = src->o_uid; + st->st_uid = src->o_uid; if (valid & OBD_MD_FLGID) - lli->lli_st_gid = src->o_gid; + st->st_gid = src->o_gid; if (valid & OBD_MD_FLFLAGS) lli->lli_st_flags = src->o_flags; if (valid & OBD_MD_FLGENER) @@ -178,51 +233,52 @@ void obdo_to_inode(struct inode *dst, struct obdo *src, obd_flag valid) void obdo_from_inode(struct obdo *dst, struct inode *src, obd_flag valid) { struct llu_inode_info *lli = llu_i2info(src); + struct intnl_stat *st = llu_i2stat(src); obd_flag newvalid = 0; if (valid & (OBD_MD_FLCTIME | OBD_MD_FLMTIME)) CDEBUG(D_INODE, "valid %x, new time %lu/%lu\n", - valid, LTIME_S(lli->lli_st_mtime), - LTIME_S(lli->lli_st_ctime)); + valid, LTIME_S(st->st_mtime), + LTIME_S(st->st_ctime)); if (valid & OBD_MD_FLATIME) { - dst->o_atime = LTIME_S(lli->lli_st_atime); + dst->o_atime = LTIME_S(st->st_atime); newvalid |= OBD_MD_FLATIME; } if (valid & OBD_MD_FLMTIME) { - dst->o_mtime = LTIME_S(lli->lli_st_mtime); + dst->o_mtime = LTIME_S(st->st_mtime); newvalid |= OBD_MD_FLMTIME; } if (valid & OBD_MD_FLCTIME) { - dst->o_ctime = LTIME_S(lli->lli_st_ctime); + dst->o_ctime = LTIME_S(st->st_ctime); newvalid |= OBD_MD_FLCTIME; } if (valid & OBD_MD_FLSIZE) { - dst->o_size = lli->lli_st_size; + dst->o_size = st->st_size; newvalid |= OBD_MD_FLSIZE; } if (valid & OBD_MD_FLBLOCKS) { /* allocation of space (x512 bytes) */ - dst->o_blocks = lli->lli_st_blocks; + dst->o_blocks = st->st_blocks; newvalid |= OBD_MD_FLBLOCKS; } if (valid & OBD_MD_FLBLKSZ) { /* optimal block size */ - dst->o_blksize = lli->lli_st_blksize; + dst->o_blksize = st->st_blksize; newvalid |= OBD_MD_FLBLKSZ; } if (valid & OBD_MD_FLTYPE) { - dst->o_mode = (dst->o_mode & S_IALLUGO)|(lli->lli_st_mode & S_IFMT); + dst->o_mode = (dst->o_mode & S_IALLUGO)|(st->st_mode & S_IFMT); newvalid |= OBD_MD_FLTYPE; } if (valid & OBD_MD_FLMODE) { - dst->o_mode = (dst->o_mode & S_IFMT)|(lli->lli_st_mode & S_IALLUGO); + dst->o_mode = (dst->o_mode & S_IFMT)|(st->st_mode & S_IALLUGO); newvalid |= OBD_MD_FLMODE; } if (valid & OBD_MD_FLUID) { - dst->o_uid = lli->lli_st_uid; + dst->o_uid = st->st_uid; newvalid |= OBD_MD_FLUID; } if (valid & OBD_MD_FLGID) { - dst->o_gid = lli->lli_st_gid; + dst->o_gid = st->st_gid; newvalid |= OBD_MD_FLGID; } if (valid & OBD_MD_FLFLAGS) { @@ -286,6 +342,16 @@ static struct inode* llu_new_inode(struct filesys *fs, { struct inode *inode; struct llu_inode_info *lli; + struct intnl_stat st = { + st_dev: 0, +#ifndef AUTOMOUNT_FILE_NAME + st_mode: fid->f_type & S_IFMT, +#else + st_mode: fid->f_type /* all of the bits! */ +#endif + st_uid: geteuid(), + st_gid: getegid(), + }; OBD_ALLOC(lli, sizeof(*lli)); if (!lli) @@ -306,13 +372,7 @@ static struct inode* llu_new_inode(struct filesys *fs, /* file identifier is needed by functions like _sysio_i_find() */ inode = _sysio_i_new(fs, &lli->lli_sysio_fid, -#ifndef AUTOMOUNT_FILE_NAME - fid->f_type & S_IFMT, -#else - fid->f_type, /* all of the bits! */ -#endif - 0, 0, - &llu_inode_ops, lli); + &st, 0, &llu_inode_ops, lli); if (!inode) OBD_FREE(lli, sizeof(*lli)); @@ -333,7 +393,7 @@ static int llu_have_md_lock(struct inode *inode) LASSERT(inode); obddev = sbi->ll_mdc_exp->exp_obd; - res_id.name[0] = lli->lli_st_ino; + res_id.name[0] = llu_i2stat(inode)->st_ino; res_id.name[1] = lli->lli_st_generation; CDEBUG(D_INFO, "trying to match res "LPU64"\n", res_id.name[0]); @@ -356,7 +416,6 @@ static int llu_have_md_lock(struct inode *inode) static int llu_inode_revalidate(struct inode *inode) { - struct llu_inode_info *lli = llu_i2info(inode); struct lov_stripe_md *lsm = NULL; ENTRY; @@ -370,19 +429,19 @@ static int llu_inode_revalidate(struct inode *inode) struct ptlrpc_request *req = NULL; struct llu_sb_info *sbi = llu_i2sbi(inode); struct ll_fid fid; - unsigned long valid = 0; + unsigned long valid = OBD_MD_FLGETATTR; int rc, ealen = 0; /* Why don't we update all valid MDS fields here, if we're * doing an RPC anyways? -phil */ - if (S_ISREG(lli->lli_st_mode)) { + if (S_ISREG(llu_i2stat(inode)->st_mode)) { ealen = obd_size_diskmd(sbi->ll_osc_exp, NULL); valid |= OBD_MD_FLEASIZE; } ll_inode2fid(&fid, inode); rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid, ealen, &req); if (rc) { - CERROR("failure %d inode %lu\n", rc, lli->lli_st_ino); + CERROR("failure %d inode %llu\n", rc, llu_i2stat(inode)->st_ino); RETURN(-abs(rc)); } rc = mdc_req2lustre_md(req, 0, sbi->ll_osc_exp, &md); @@ -423,21 +482,7 @@ static int llu_inode_revalidate(struct inode *inode) static void copy_stat_buf(struct inode *ino, struct intnl_stat *b) { - struct llu_inode_info *lli = llu_i2info(ino); - - b->st_dev = lli->lli_st_dev; - b->st_ino = lli->lli_st_ino; - b->st_mode = lli->lli_st_mode; - b->st_nlink = lli->lli_st_nlink; - b->st_uid = lli->lli_st_uid; - b->st_gid = lli->lli_st_gid; - b->st_rdev = lli->lli_st_rdev; - b->st_size = lli->lli_st_size; - b->st_blksize = lli->lli_st_blksize; - b->st_blocks = lli->lli_st_blocks; - b->st_atime = lli->lli_st_atime; - b->st_mtime = lli->lli_st_mtime; - b->st_ctime = lli->lli_st_ctime; + *b = *llu_i2stat(ino); } static int llu_iop_getattr(struct pnode *pno, @@ -447,6 +492,8 @@ static int llu_iop_getattr(struct pnode *pno, int rc; ENTRY; + liblustre_wait_event(0); + if (!ino) { LASSERT(pno); LASSERT(pno->p_base->pb_ino); @@ -461,14 +508,7 @@ static int llu_iop_getattr(struct pnode *pno, rc = llu_inode_revalidate(ino); if (!rc) { copy_stat_buf(ino, b); - - if (llu_i2info(ino)->lli_it) { - struct lookup_intent *it; - - LL_GET_INTENT(ino, it); - it->it_op_release(it); - OBD_FREE(it, sizeof(*it)); - } + LASSERT(!llu_i2info(ino)->lli_it); } RETURN(rc); @@ -480,7 +520,8 @@ static int null_if_equal(struct ldlm_lock *lock, void *data) lock->l_ast_data = NULL; if (lock->l_req_mode != lock->l_granted_mode) - LDLM_ERROR(lock,"clearing inode with ungranted lock\n"); } + LDLM_ERROR(lock,"clearing inode with ungranted lock\n"); + } return LDLM_ITER_CONTINUE; } @@ -492,8 +533,8 @@ void llu_clear_inode(struct inode *inode) struct llu_sb_info *sbi = llu_i2sbi(inode); ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%lu(%p)\n", lli->lli_st_ino, - lli->lli_st_generation, inode); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu/%lu(%p)\n", + llu_i2stat(inode)->st_ino, lli->lli_st_generation, inode); ll_inode2fid(&fid, inode); clear_bit(LLI_F_HAVE_MDS_SIZE_LOCK, &(lli->lli_flags)); @@ -522,6 +563,7 @@ void llu_iop_gone(struct inode *inode) struct llu_inode_info *lli = llu_i2info(inode); ENTRY; + liblustre_wait_event(0); llu_clear_inode(inode); OBD_FREE(lli, sizeof(*lli)); @@ -531,7 +573,7 @@ void llu_iop_gone(struct inode *inode) static int inode_setattr(struct inode * inode, struct iattr * attr) { unsigned int ia_valid = attr->ia_valid; - struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); int error = 0; if (ia_valid & ATTR_SIZE) { @@ -541,19 +583,19 @@ static int inode_setattr(struct inode * inode, struct iattr * attr) } if (ia_valid & ATTR_UID) - lli->lli_st_uid = attr->ia_uid; + st->st_uid = attr->ia_uid; if (ia_valid & ATTR_GID) - lli->lli_st_gid = attr->ia_gid; + st->st_gid = attr->ia_gid; if (ia_valid & ATTR_ATIME) - lli->lli_st_atime = attr->ia_atime; + st->st_atime = attr->ia_atime; if (ia_valid & ATTR_MTIME) - lli->lli_st_mtime = attr->ia_mtime; + st->st_mtime = attr->ia_mtime; if (ia_valid & ATTR_CTIME) - lli->lli_st_ctime = attr->ia_ctime; + st->st_ctime = attr->ia_ctime; if (ia_valid & ATTR_MODE) { - lli->lli_st_mode = attr->ia_mode; - if (!in_group_p(lli->lli_st_gid) && !capable(CAP_FSETID)) - lli->lli_st_mode &= ~S_ISGID; + st->st_mode = attr->ia_mode; + if (!in_group_p(st->st_gid) && !capable(CAP_FSETID)) + st->st_mode &= ~S_ISGID; } /* mark_inode_dirty(inode); */ out: @@ -577,14 +619,14 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr) { struct lov_stripe_md *lsm = llu_i2info(inode)->lli_smd; struct llu_sb_info *sbi = llu_i2sbi(inode); - struct llu_inode_info *lli = llu_i2info(inode); + struct intnl_stat *st = llu_i2stat(inode); struct ptlrpc_request *request = NULL; struct mdc_op_data op_data; int ia_valid = attr->ia_valid; int rc = 0; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu\n", lli->lli_st_ino); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%llu\n", st->st_ino); if (ia_valid & ATTR_SIZE) { if (attr->ia_size > ll_file_maxbytes(inode)) { @@ -639,10 +681,18 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr) ptlrpc_req_finished(request); RETURN(rc); } + + /* We call inode_setattr to adjust timestamps, but we first + * clear ATTR_SIZE to avoid invoking vmtruncate. + * + * NB: ATTR_SIZE will only be set at this point if the size + * resides on the MDS, ie, this file has no objects. */ + attr->ia_valid &= ~ATTR_SIZE; + inode_setattr(inode, attr); llu_update_inode(inode, md.body, md.lsm); ptlrpc_req_finished(request); - if (!md.lsm || !S_ISREG(lli->lli_st_mode)) { + if (!md.lsm || !S_ISREG(st->st_mode)) { CDEBUG(D_INODE, "no lsm: not setting attrs on OST\n"); RETURN(0); } @@ -654,12 +704,12 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr) if (ia_valid & (ATTR_MTIME | ATTR_ATIME)) { /* from sys_utime() */ if (!(ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET))) { - if (current->fsuid != lli->lli_st_uid && - (rc = ll_permission(inode, 0/*MAY_WRITE*/, NULL)) != 0) + if (current->fsuid != st->st_uid && + (rc = ll_permission(inode, MAY_WRITE)) != 0) RETURN(rc); } else { /* from inode_change_ok() */ - if (current->fsuid != lli->lli_st_uid && + if (current->fsuid != st->st_uid && !capable(CAP_FOWNER)) RETURN(-EPERM); } @@ -689,9 +739,6 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr) } rc = llu_vmtruncate(inode, attr->ia_size); - if (rc == 0) - set_bit(LLI_F_HAVE_OST_SIZE_LOCK, - &llu_i2info(inode)->lli_flags); /* unlock now as we don't mind others file lockers racing with * the mds updates below? */ @@ -704,8 +751,8 @@ int llu_setattr_raw(struct inode *inode, struct iattr *attr) } else if (ia_valid & (ATTR_MTIME | ATTR_MTIME_SET)) { struct obdo oa; - CDEBUG(D_INODE, "set mtime on OST inode %lu to %lu\n", - lli->lli_st_ino, LTIME_S(attr->ia_mtime)); + CDEBUG(D_INODE, "set mtime on OST inode %llu to %lu\n", + st->st_ino, LTIME_S(attr->ia_mtime)); oa.o_id = lsm->lsm_object_id; oa.o_valid = OBD_MD_FLID; obdo_from_inode(&oa, inode, OBD_MD_FLTYPE | OBD_MD_FLATIME | @@ -728,6 +775,11 @@ static int llu_iop_setattr(struct pnode *pno, struct iattr iattr; ENTRY; + liblustre_wait_event(0); + + LASSERT(!(mask & ~(SETATTR_MTIME | SETATTR_ATIME | + SETATTR_UID | SETATTR_GID | + SETATTR_LEN | SETATTR_MODE))); memset(&iattr, 0, sizeof(iattr)); if (mask & SETATTR_MODE) { @@ -755,7 +807,8 @@ static int llu_iop_setattr(struct pnode *pno, iattr.ia_valid |= ATTR_SIZE; } - iattr.ia_valid |= ATTR_RAW; + iattr.ia_valid |= ATTR_RAW | ATTR_CTIME; + iattr.ia_ctime = CURRENT_TIME; RETURN(llu_setattr_raw(ino, &iattr)); } @@ -774,7 +827,7 @@ static int llu_iop_symlink_raw(struct pnode *pno, const char *tgt) int err = -EMLINK; ENTRY; - if (llu_i2info(dir)->lli_st_nlink >= EXT2_LINK_MAX) + if (llu_i2stat(dir)->st_nlink >= EXT2_LINK_MAX) RETURN(err); llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0); @@ -793,7 +846,8 @@ static int llu_readlink_internal(struct inode *inode, struct llu_sb_info *sbi = llu_i2sbi(inode); struct ll_fid fid; struct mds_body *body; - int rc, symlen = lli->lli_st_size + 1; + struct intnl_stat *st = llu_i2stat(inode); + int rc, symlen = st->st_size + 1; ENTRY; *request = NULL; @@ -808,7 +862,7 @@ static int llu_readlink_internal(struct inode *inode, rc = mdc_getattr(sbi->ll_mdc_exp, &fid, OBD_MD_LINKNAME, symlen, request); if (rc) { - CERROR("inode %lu: rc = %d\n", lli->lli_st_ino, rc); + CERROR("inode %llu: rc = %d\n", st->st_ino, rc); RETURN(rc); } @@ -823,8 +877,8 @@ static int llu_readlink_internal(struct inode *inode, LASSERT (symlen != 0); if (body->eadatasize != symlen) { - CERROR ("inode %lu: symlink length %d not expected %d\n", - lli->lli_st_ino, body->eadatasize - 1, symlen - 1); + CERROR ("inode %llu: symlink length %d not expected %d\n", + st->st_ino, body->eadatasize - 1, symlen - 1); GOTO (failed, rc = -EPROTO); } @@ -832,8 +886,8 @@ static int llu_readlink_internal(struct inode *inode, if (*symname == NULL || strnlen (*symname, symlen) != symlen - 1) { /* not full/NULL terminated */ - CERROR ("inode %lu: symlink not NULL terminated string" - "of length %d\n", lli->lli_st_ino, symlen - 1); + CERROR ("inode %llu: symlink not NULL terminated string" + "of length %d\n", st->st_ino, symlen - 1); GOTO (failed, rc = -EPROTO); } @@ -863,6 +917,7 @@ static int llu_iop_readlink(struct pnode *pno, char *data, size_t bufsize) LASSERT(symname); strncpy(data, symname, bufsize); + rc = strlen(symname); ptlrpc_req_finished(request); out: @@ -880,14 +935,13 @@ static int llu_iop_mknod_raw(struct pnode *pno, int err = -EMLINK; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu\n", - pno->p_base->pb_name.name, llu_i2info(dir)->lli_st_ino); + CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu\n", + (int)pno->p_base->pb_name.len, pno->p_base->pb_name.name, + llu_i2stat(dir)->st_ino); - if (llu_i2info(dir)->lli_st_nlink >= EXT2_LINK_MAX) + if (llu_i2stat(dir)->st_nlink >= EXT2_LINK_MAX) RETURN(err); - mode &= ~current->fs->umask; - switch (mode & S_IFMT) { case 0: case S_IFREG: @@ -927,13 +981,18 @@ static int llu_iop_link_raw(struct pnode *old, struct pnode *new) LASSERT(src); LASSERT(dir); + liblustre_wait_event(0); llu_prepare_mdc_op_data(&op_data, src, dir, name, namelen, 0); rc = mdc_link(llu_i2sbi(src)->ll_mdc_exp, &op_data, &request); ptlrpc_req_finished(request); + liblustre_wait_event(0); RETURN(rc); } +/* + * libsysio will clear the inode immediately after return + */ static int llu_iop_unlink_raw(struct pnode *pno) { struct inode *dir = pno->p_base->pb_parent->pb_ino; @@ -948,30 +1007,21 @@ static int llu_iop_unlink_raw(struct pnode *pno) LASSERT(target); + liblustre_wait_event(0); llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0); rc = mdc_unlink(llu_i2sbi(dir)->ll_mdc_exp, &op_data, &request); - if (!rc) { + if (!rc) rc = llu_objects_destroy(request, dir); - - llu_i2info(target)->lli_stale_flag = 1; - unhook_stale_inode(pno); - } - ptlrpc_req_finished(request); + liblustre_wait_event(0); + RETURN(rc); } -/* FIXME - * following cases need to be considered later: - * - rename an opened file/dir - * - an opened file be removed in rename - * - rename to remove and hardlink (?opened) - */ static int llu_iop_rename_raw(struct pnode *old, struct pnode *new) { struct inode *src = old->p_parent->p_base->pb_ino; struct inode *tgt = new->p_parent->p_base->pb_ino; - struct inode *tgtinode = new->p_base->pb_ino; const char *oldname = old->p_base->pb_name.name; int oldnamelen = old->p_base->pb_name.len; const char *newname = new->p_base->pb_name.name; @@ -990,11 +1040,6 @@ static int llu_iop_rename_raw(struct pnode *old, struct pnode *new) &request); if (!rc) { rc = llu_objects_destroy(request, src); - - if (tgtinode) { - llu_i2info(tgtinode)->lli_stale_flag = 1; - unhook_stale_inode(new); - } } ptlrpc_req_finished(request); @@ -1088,6 +1133,8 @@ static int llu_iop_statvfs(struct pnode *pno, int rc; ENTRY; + liblustre_wait_event(0); + #ifndef __CYGWIN__ LASSERT(pno->p_base->pb_ino); rc = llu_statfs(llu_i2sbi(pno->p_base->pb_ino), &fs); @@ -1119,17 +1166,16 @@ static int llu_iop_mkdir_raw(struct pnode *pno, mode_t mode) const char *name = qstr->name; int len = qstr->len; struct ptlrpc_request *request = NULL; - struct llu_inode_info *lli = llu_i2info(dir); + struct intnl_stat *st = llu_i2stat(dir); struct mdc_op_data op_data; int err = -EMLINK; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu/%lu(%p)\n", - name, lli->lli_st_ino, lli->lli_st_generation, dir); + CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu/%lu(%p)\n", + len, name, st->st_ino, llu_i2info(dir)->lli_st_generation, dir); - if (lli->lli_st_nlink >= EXT2_LINK_MAX) + if (st->st_nlink >= EXT2_LINK_MAX) RETURN(err); - mode = (mode & (S_IRWXUGO|S_ISVTX) & ~current->fs->umask) | S_IFDIR; llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, 0); err = mdc_create(llu_i2sbi(dir)->ll_mdc_exp, &op_data, NULL, 0, mode, current->fsuid, current->fsgid, 0, &request); @@ -1145,35 +1191,338 @@ static int llu_iop_rmdir_raw(struct pnode *pno) int len = qstr->len; struct ptlrpc_request *request = NULL; struct mdc_op_data op_data; - struct llu_inode_info *lli = llu_i2info(dir); int rc; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:name=%s,dir=%lu/%lu(%p)\n", - name, lli->lli_st_ino, lli->lli_st_generation, dir); + CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%llu/%lu(%p)\n", len, name, + llu_i2stat(dir)->st_ino, llu_i2info(dir)->lli_st_generation,dir); llu_prepare_mdc_op_data(&op_data, dir, NULL, name, len, S_IFDIR); rc = mdc_unlink(llu_i2sbi(dir)->ll_mdc_exp, &op_data, &request); ptlrpc_req_finished(request); - /* libsysio: remove the pnode right away */ - if (!rc) { - llu_i2info(pno->p_base->pb_ino)->lli_stale_flag = 1; - unhook_stale_inode(pno); + RETURN(rc); +} + +#ifdef O_DIRECT +#define FCNTL_FLMASK (O_APPEND|O_NONBLOCK|O_ASYNC|O_DIRECT) +#else +#define FCNTL_FLMASK (O_APPEND|O_NONBLOCK|O_ASYNC) +#endif +#define FCNTL_FLMASK_INVALID (O_NONBLOCK|O_ASYNC) + +/* refer to ll_file_flock() for details */ +static int llu_file_flock(struct inode *ino, + int cmd, + struct file_lock *file_lock) +{ + struct obd_device *obddev; + struct llu_inode_info *lli = llu_i2info(ino); + struct intnl_stat *st = llu_i2stat(ino); + struct ldlm_res_id res_id = + { .name = {st->st_ino, + lli->lli_st_generation, LDLM_FLOCK} }; + struct lustre_handle lockh = {0}; + ldlm_policy_data_t flock; + ldlm_mode_t mode = 0; + int flags = 0; + int rc; + + CDEBUG(D_VFSTRACE, "VFS Op:inode="LPU64" file_lock=%p\n", + st->st_ino, file_lock); + + flock.l_flock.pid = file_lock->fl_pid; + flock.l_flock.start = file_lock->fl_start; + flock.l_flock.end = file_lock->fl_end; + + switch (file_lock->fl_type) { + case F_RDLCK: + mode = LCK_PR; + break; + case F_UNLCK: + mode = LCK_NL; + break; + case F_WRLCK: + mode = LCK_PW; + break; + default: + CERROR("unknown fcntl lock type: %d\n", file_lock->fl_type); + LBUG(); + } + + switch (cmd) { + case F_SETLKW: +#ifdef F_SETLKW64 +#if F_SETLKW64 != F_SETLKW + case F_SETLKW64: +#endif +#endif + flags = 0; + break; + case F_SETLK: +#ifdef F_SETLK64 +#if F_SETLK64 != F_SETLK + case F_SETLK64: +#endif +#endif + flags = LDLM_FL_BLOCK_NOWAIT; + break; + case F_GETLK: +#ifdef F_GETLK64 +#if F_GETLK64 != F_GETLK + case F_GETLK64: +#endif +#endif + flags = LDLM_FL_TEST_LOCK; + file_lock->fl_type = mode; + break; + default: + CERROR("unknown fcntl cmd: %d\n", cmd); + LBUG(); } + CDEBUG(D_DLMTRACE, "inode="LPU64", pid=%u, flags=%#x, mode=%u, " + "start="LPU64", end="LPU64"\n", st->st_ino, flock.l_flock.pid, + flags, mode, flock.l_flock.start, flock.l_flock.end); + + obddev = llu_i2mdcexp(ino)->exp_obd; + rc = ldlm_cli_enqueue(llu_i2mdcexp(ino), NULL, obddev->obd_namespace, + res_id, LDLM_FLOCK, &flock, mode, &flags, + NULL, ldlm_flock_completion_ast, NULL, file_lock, + NULL, 0, NULL, &lockh); RETURN(rc); } -static int llu_iop_fcntl(struct inode *ino, int cmd, va_list ap) +static int assign_type(struct file_lock *fl, int type) { - CERROR("liblustre did not support fcntl\n"); - return -ENOSYS; + switch (type) { + case F_RDLCK: + case F_WRLCK: + case F_UNLCK: + fl->fl_type = type; + return 0; + default: + return -EINVAL; + } +} + +static int flock_to_posix_lock(struct inode *ino, + struct file_lock *fl, + struct flock *l) +{ + switch (l->l_whence) { + /* XXX: only SEEK_SET is supported in lustre */ + case SEEK_SET: + fl->fl_start = 0; + break; + default: + return -EINVAL; + } + + fl->fl_end = l->l_len - 1; + if (l->l_len < 0) + return -EINVAL; + if (l->l_len == 0) + fl->fl_end = OFFSET_MAX; + + fl->fl_pid = getpid(); + fl->fl_flags = FL_POSIX; + fl->fl_notify = NULL; + fl->fl_insert = NULL; + fl->fl_remove = NULL; + /* XXX: these fields can't be filled with suitable values, + but I think lustre doesn't use them. + */ + fl->fl_owner = NULL; + fl->fl_file = NULL; + + return assign_type(fl, l->l_type); } +static int llu_fcntl_getlk(struct inode *ino, struct flock *flock) +{ + struct file_lock fl; + int error; + + error = EINVAL; + if ((flock->l_type != F_RDLCK) && (flock->l_type != F_WRLCK)) + goto out; + + error = flock_to_posix_lock(ino, &fl, flock); + if (error) + goto out; + + error = llu_file_flock(ino, F_GETLK, &fl); + if (error) + goto out; + + flock->l_type = F_UNLCK; + if (fl.fl_type != F_UNLCK) { + flock->l_pid = fl.fl_pid; + flock->l_start = fl.fl_start; + flock->l_len = fl.fl_end == OFFSET_MAX ? 0: + fl.fl_end - fl.fl_start + 1; + flock->l_whence = SEEK_SET; + flock->l_type = fl.fl_type; + } + +out: + return error; +} + +static int llu_fcntl_setlk(struct inode *ino, int cmd, struct flock *flock) +{ + struct file_lock fl; + int flags = llu_i2info(ino)->lli_open_flags + 1; + int error; + + error = flock_to_posix_lock(ino, &fl, flock); + if (error) + goto out; + if (cmd == F_SETLKW) + fl.fl_flags |= FL_SLEEP; + + error = -EBADF; + switch (flock->l_type) { + case F_RDLCK: + if (!(flags & FMODE_READ)) + goto out; + break; + case F_WRLCK: + if (!(flags & FMODE_WRITE)) + goto out; + break; + case F_UNLCK: + break; + default: + error = -EINVAL; + goto out; + } + + error = llu_file_flock(ino, cmd, &fl); + if (error) + goto out; + +out: + return error; +} + +static int llu_iop_fcntl(struct inode *ino, int cmd, va_list ap, int *rtn) +{ + struct llu_inode_info *lli = llu_i2info(ino); + long flags; + struct flock *flock; + long err; + + switch (cmd) { + case F_GETFL: + *rtn = lli->lli_open_flags; + return 0; + case F_SETFL: + flags = va_arg(ap, long); + flags &= FCNTL_FLMASK; + if (flags & FCNTL_FLMASK_INVALID) { + CERROR("liblustre don't support O_NONBLOCK, O_ASYNC, " + "and O_DIRECT on file descriptor\n"); + *rtn = -EINVAL; + return EINVAL; + } + lli->lli_open_flags = (int)(flags & FCNTL_FLMASK) | + (lli->lli_open_flags & ~FCNTL_FLMASK); + *rtn = 0; + return 0; + case F_GETLK: + flock = va_arg(ap, struct flock *); + err = llu_fcntl_getlk(ino, flock); + *rtn = err? -1: 0; + return err; + case F_SETLK: + case F_SETLKW: + flock = va_arg(ap, struct flock *); + err = llu_fcntl_setlk(ino, cmd, flock); + *rtn = err? -1: 0; + return err; + } + + CERROR("unsupported fcntl cmd %x\n", cmd); + *rtn = -ENOSYS; + return ENOSYS; +} + +static int llu_get_grouplock(struct inode *inode, unsigned long arg) +{ + struct llu_inode_info *lli = llu_i2info(inode); + struct ll_file_data *fd = lli->lli_file_data; + ldlm_policy_data_t policy = { .l_extent = { .start = 0, + .end = OBD_OBJECT_EOF}}; + struct lustre_handle lockh = { 0 }; + struct lov_stripe_md *lsm = lli->lli_smd; + ldlm_error_t err; + int flags = 0; + ENTRY; + + if (fd->fd_flags & LL_FILE_GROUP_LOCKED) { + RETURN(-EINVAL); + } + + policy.l_extent.gid = arg; + if (lli->lli_open_flags & O_NONBLOCK) + flags = LDLM_FL_BLOCK_NOWAIT; + + err = llu_extent_lock(fd, inode, lsm, LCK_GROUP, &policy, &lockh, + flags); + if (err) + RETURN(err); + + fd->fd_flags |= LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK; + fd->fd_gid = arg; + memcpy(&fd->fd_cwlockh, &lockh, sizeof(lockh)); + + RETURN(0); +} + +static int llu_put_grouplock(struct inode *inode, unsigned long arg) +{ + struct llu_inode_info *lli = llu_i2info(inode); + struct ll_file_data *fd = lli->lli_file_data; + struct lov_stripe_md *lsm = lli->lli_smd; + ldlm_error_t err; + ENTRY; + + if (!(fd->fd_flags & LL_FILE_GROUP_LOCKED)) + RETURN(-EINVAL); + + if (fd->fd_gid != arg) + RETURN(-EINVAL); + + fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK); + + err = llu_extent_unlock(fd, inode, lsm, LCK_GROUP, &fd->fd_cwlockh); + if (err) + RETURN(err); + + fd->fd_gid = 0; + memset(&fd->fd_cwlockh, 0, sizeof(fd->fd_cwlockh)); + + RETURN(0); +} + static int llu_iop_ioctl(struct inode *ino, unsigned long int request, va_list ap) { - CERROR("liblustre did not support ioctl\n"); + + liblustre_wait_event(0); + + switch (request) { + unsigned long arg; + case LL_IOC_GROUP_LOCK: + arg = va_arg(ap, unsigned long); + return llu_get_grouplock(ino, arg); + case LL_IOC_GROUP_UNLOCK: + arg = va_arg(ap, unsigned long); + return llu_put_grouplock(ino, arg); + } + + CERROR("did not support ioctl cmd %lx\n", request); return -ENOSYS; } @@ -1182,11 +1531,13 @@ static int llu_iop_ioctl(struct inode *ino, unsigned long int request, */ static int llu_iop_sync(struct inode *inode) { + liblustre_wait_event(0); return 0; } static int llu_iop_datasync(struct inode *inode) { + liblustre_wait_event(0); return 0; } @@ -1203,8 +1554,11 @@ struct inode *llu_iget(struct filesys *fs, struct lustre_md *md) if ((md->body->valid & (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE)) != - (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE)) - CERROR("invalide fields!\n"); + (OBD_MD_FLGENER | OBD_MD_FLID | OBD_MD_FLTYPE)) { + CERROR("bad md body valid mask "LPX64"\n", md->body->valid); + LBUG(); + return ERR_PTR(-EPERM); + } /* try to find existing inode */ fid.id = md->body->ino; @@ -1215,9 +1569,10 @@ struct inode *llu_iget(struct filesys *fs, struct lustre_md *md) if (inode) { struct llu_inode_info *lli = llu_i2info(inode); - if (lli->lli_stale_flag || - lli->lli_st_generation != md->body->generation) + if (inode->i_zombie || + lli->lli_st_generation != md->body->generation) { I_RELE(inode); + } else { llu_update_inode(inode, md->body, md->lsm); return inode; @@ -1253,12 +1608,26 @@ llu_fsswop_mount(const char *source, struct lustre_handle osc_conn = {0, }; struct lustre_md md; class_uuid_t uuid; + struct config_llog_instance cfg; + char ll_instance[sizeof(sbi) * 2 + 1]; struct lustre_profile *lprof; + char *zconf_mdsnid, *zconf_mdsname, *zconf_profile; char *osc = NULL, *mdc = NULL; - int err = -EINVAL; + int async = 1, err = -EINVAL; ENTRY; + if (ll_parse_mount_target(source, + &zconf_mdsnid, + &zconf_mdsname, + &zconf_profile)) { + CERROR("mal-formed target %s\n", source); + RETURN(err); + } + if (!zconf_mdsnid || !zconf_mdsname || !zconf_profile) { + printf("Liblustre: invalid target %s\n", source); + RETURN(err); + } /* allocate & initialize sbi */ OBD_ALLOC(sbi, sizeof(*sbi)); if (!sbi) @@ -1268,61 +1637,30 @@ llu_fsswop_mount(const char *source, generate_random_uuid(uuid); class_uuid_unparse(uuid, &sbi->ll_sb_uuid); - /* zeroconf */ - if (g_zconf) { - struct config_llog_instance cfg; - int len; - - if (!g_zconf_mdsname) { - CERROR("no mds name\n"); - GOTO(out_free, err = -EINVAL); - } - - /* generate a string unique to this super, let's try - the address of the super itself.*/ - len = (sizeof(sbi) * 2) + 1; - OBD_ALLOC(sbi->ll_instance, len); - if (sbi->ll_instance == NULL) - GOTO(out_free, err = -ENOMEM); - sprintf(sbi->ll_instance, "%p", sbi); - - cfg.cfg_instance = sbi->ll_instance; - cfg.cfg_uuid = sbi->ll_sb_uuid; - err = liblustre_process_log(&cfg, 1); - if (err < 0) { - CERROR("Unable to process log: %s\n", g_zconf_profile); - - GOTO(out_free, err); - } - - lprof = class_get_profile(g_zconf_profile); - if (lprof == NULL) { - CERROR("No profile found: %s\n", g_zconf_profile); - GOTO(out_free, err = -EINVAL); - } - if (osc) - OBD_FREE(osc, strlen(osc) + 1); - OBD_ALLOC(osc, strlen(lprof->lp_osc) + - strlen(sbi->ll_instance) + 2); - sprintf(osc, "%s-%s", lprof->lp_osc, sbi->ll_instance); - - if (mdc) - OBD_FREE(mdc, strlen(mdc) + 1); - OBD_ALLOC(mdc, strlen(lprof->lp_mdc) + - strlen(sbi->ll_instance) + 2); - sprintf(mdc, "%s-%s", lprof->lp_mdc, sbi->ll_instance); - } else { - /* setup from dump_file */ - if (list_empty(&lustre_profile_list)) { - CERROR("no profile\n"); - GOTO(out_free, err = -EINVAL); - } + /* generate a string unique to this super, let's try + the address of the super itself.*/ + sprintf(ll_instance, "%p", sbi); + + /* retrive & parse config log */ + cfg.cfg_instance = ll_instance; + cfg.cfg_uuid = sbi->ll_sb_uuid; + err = liblustre_process_log(&cfg, zconf_mdsnid, zconf_mdsname, + zconf_profile, 1); + if (err < 0) { + CERROR("Unable to process log: %s\n", zconf_profile); + GOTO(out_free, err); + } - lprof = list_entry(lustre_profile_list.next, - struct lustre_profile, lp_list); - osc = lprof->lp_osc; - mdc = lprof->lp_mdc; + lprof = class_get_profile(zconf_profile); + if (lprof == NULL) { + CERROR("No profile found: %s\n", zconf_profile); + GOTO(out_free, err = -EINVAL); } + OBD_ALLOC(osc, strlen(lprof->lp_osc) + strlen(ll_instance) + 2); + sprintf(osc, "%s-%s", lprof->lp_osc, ll_instance); + + OBD_ALLOC(mdc, strlen(lprof->lp_mdc) + strlen(ll_instance) + 2); + sprintf(mdc, "%s-%s", lprof->lp_mdc, ll_instance); if (!osc) { CERROR("no osc\n"); @@ -1344,6 +1682,8 @@ llu_fsswop_mount(const char *source, CERROR("MDC %s: not setup or attached\n", mdc); GOTO(out_free, err = -EINVAL); } + obd_set_info(obd->obd_self_export, strlen("async"), "async", + sizeof(async), &async); /* setup mdc */ err = obd_connect(&mdc_conn, obd, &sbi->ll_sb_uuid, NULL /* ocd */); @@ -1367,6 +1707,8 @@ llu_fsswop_mount(const char *source, CERROR("OSC %s: not setup or attached\n", osc); GOTO(out_mdc, err = -EINVAL); } + obd_set_info(obd->obd_self_export, strlen("async"), "async", + sizeof(async), &async); err = obd_connect(&osc_conn, obd, &sbi->ll_sb_uuid, NULL /* ocd */); if (err) { @@ -1387,7 +1729,7 @@ llu_fsswop_mount(const char *source, /* fetch attr of root inode */ err = mdc_getattr(sbi->ll_mdc_exp, &rootfid, - OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, 0, &request); + OBD_MD_FLGETATTR | OBD_MD_FLBLOCKS, 0, &request); if (err) { CERROR("mdc_getattr failed for root: rc = %d\n", err); GOTO(out_osc, err); @@ -1402,7 +1744,7 @@ llu_fsswop_mount(const char *source, LASSERT(sbi->ll_rootino != 0); root = llu_iget(fs, &md); - if (root == NULL) { + if (!root || IS_ERR(root)) { CERROR("fail to generate root inode\n"); GOTO(out_request, err = -EBADF); } @@ -1424,7 +1766,7 @@ llu_fsswop_mount(const char *source, ptlrpc_req_finished(request); - printf("LibLustre: namespace mounted successfully!\n"); + CDEBUG(D_SUPER, "LibLustre: %s mounted successfully!\n", source); return 0; @@ -1437,6 +1779,10 @@ out_osc: out_mdc: obd_disconnect(sbi->ll_mdc_exp); out_free: + if (osc) + OBD_FREE(osc, strlen(osc) + 1); + if (mdc) + OBD_FREE(mdc, strlen(mdc) + 1); OBD_FREE(sbi, sizeof(*sbi)); return err; } @@ -1459,8 +1805,9 @@ static struct inode_ops llu_inode_ops = { inop_link: llu_iop_link_raw, inop_unlink: llu_iop_unlink_raw, inop_rename: llu_iop_rename_raw, - inop_ipreadv: llu_iop_ipreadv, - inop_ipwritev: llu_iop_ipwritev, + inop_pos: llu_iop_pos, + inop_read: llu_iop_read, + inop_write: llu_iop_write, inop_iodone: llu_iop_iodone, inop_fcntl: llu_iop_fcntl, inop_sync: llu_iop_sync, @@ -1472,5 +1819,3 @@ static struct inode_ops llu_inode_ops = { #endif inop_gone: llu_iop_gone, }; - -#warning "time_after() defined in liblustre.h need to be rewrite in userspace" diff --git a/lustre/liblustre/tests/Makefile.am b/lustre/liblustre/tests/Makefile.am index 0a9a1c0..616fea4 100644 --- a/lustre/liblustre/tests/Makefile.am +++ b/lustre/liblustre/tests/Makefile.am @@ -2,47 +2,53 @@ AM_CPPFLAGS = -I$(SYSIO)/include -I/opt/lam/include $(LLCPPFLAGS) -I$(top_srcdir)/portals/unals AM_CFLAGS = $(LLCFLAGS) -LIBS = $(LIBEFENCE) $(LIBREADLINE) +AM_LIBS = $(LIBEFENCE) $(LIBREADLINE) -LLIB_EXEC= ../liblustre.a -lpthread +LLIB_EXEC= $(top_builddir)/lustre/liblustre/liblustre.a $(CAP_LIBS) $(PTHREAD_LIBS) if LIBLUSTRE noinst_LIBRARIES = libtestcommon.a -def_tests = echo_test sanity recovery_small replay_single replay_ost_single + +if LIBLUSTRE_TESTS +noinst_PROGRAMS = sanity recovery_small replay_single replay_ost_single + +if TESTS +noinst_PROGRAMS += echo_test +endif # TESTS if MPITESTS -noinst_PROGRAMS = $(def_tests) test_lock_cancel -else -noinst_PROGRAMS = $(def_tests) -endif +noinst_PROGRAMS += test_lock_cancel +endif # MPITESTS + +endif # LIBLUSTRE_TESTS endif # LIBLUSTRE libtestcommon_a_SOURCES = test_common.c test_common.h -echo_test_SOURCES = echo_test.c $(top_srcdir)/lustre/utils/parser.c $(top_srcdir)/lustre/utils/obd.c $(top_srcdir)/lustre/utils/lustre_cfg.c +echo_test_SOURCES = echo_test.c $(top_srcdir)/lustre/utils/parser.c $(top_srcdir)/lustre/utils/obd.c $(top_srcdir)/lustre/utils/lustre_cfg.c echo_test_CFLAGS = $(LL_CFLAGS) -echo_test_LDADD = ../liblsupport.a $(LIBREADLINE) -lpthread +echo_test_LDADD = $(top_builddir)/lustre/liblustre/liblsupport.a $(LIBREADLINE) $(CAP_LIBS) $(PTHREAD_LIBS) echo_test_DEPENDENCIES=$(top_builddir)/lustre/liblustre/liblsupport.a sanity_SOURCES = sanity.c sanity_CFLAGS = $(LL_CFLAGS) -sanity_LDADD := ./libtestcommon.a $(LLIB_EXEC) -sanity_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a ./libtestcommon.a +sanity_LDADD := libtestcommon.a $(LLIB_EXEC) +sanity_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a recovery_small_SOURCES = recovery_small.c recovery_small_CFLAGS = $(LL_CFLAGS) -recovery_small_LDADD := ./libtestcommon.a $(LLIB_EXEC) -recovery_small_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a +recovery_small_LDADD := libtestcommon.a $(LLIB_EXEC) +recovery_small_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a replay_single_SOURCES = replay_single.c replay_single_CFLAGS = $(LL_CFLAGS) -replay_single_LDADD := ./libtestcommon.a $(LLIB_EXEC) -replay_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a +replay_single_LDADD := libtestcommon.a $(LLIB_EXEC) +replay_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a replay_ost_single_SOURCES = replay_ost_single.c replay_ost_single_CFLAGS = $(LL_CFLAGS) -replay_ost_single_LDADD := ./libtestcommon.a $(LLIB_EXEC) -replay_ost_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a +replay_ost_single_LDADD := libtestcommon.a $(LLIB_EXEC) +replay_ost_single_DEPENDENCIES = $(top_builddir)/lustre/liblustre/liblustre.a libtestcommon.a if MPITESTS test_lock_cancel_SOURCES = test_lock_cancel.c diff --git a/lustre/liblustre/tests/echo_test.c b/lustre/liblustre/tests/echo_test.c index 9720c63..b45f18e 100644 --- a/lustre/liblustre/tests/echo_test.c +++ b/lustre/liblustre/tests/echo_test.c @@ -1,64 +1,39 @@ -#include <stdio.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <arpa/inet.h> - -#include <portals/api-support.h> /* needed for ptpctl.h */ -#include <portals/ptlctl.h> /* needed for parse_dump */ - +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * Lustre Light user test program + * + * Copyright (c) 2002-2004 Cluster File Systems, Inc. + * + * This file is part of Lustre, http://www.lustre.org. + * + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ #include <liblustre.h> #include <linux/obd.h> #include <linux/obd_class.h> -#include <procbridge.h> #include <linux/obd_ost.h> #define LIBLUSTRE_TEST 1 #include "../utils/lctl.c" -struct ldlm_namespace; -struct ldlm_res_id; -struct obd_import; - -unsigned int portal_subsystem_debug = ~0 - (S_PORTALS | S_NAL); - -void *inter_module_get(char *arg) -{ - if (!strcmp(arg, "ldlm_cli_cancel_unused")) - return ldlm_cli_cancel_unused; - else if (!strcmp(arg, "ldlm_namespace_cleanup")) - return ldlm_namespace_cleanup; - else if (!strcmp(arg, "ldlm_replay_locks")) - return ldlm_replay_locks; - else - return NULL; -} - -struct task_struct *current; - -int init_current(int argc, char **argv) -{ - current = malloc(sizeof(*current)); - strncpy(current->comm, argv[0], sizeof(current->comm)); - current->pid = getpid(); - return 0; -} - -ptl_nid_t tcpnal_mynid; - -int init_lib_portals() -{ - int rc; - - rc = PtlInit(); - if (rc != PTL_OK) - CERROR("PtlInit failed: error %d\n", rc); - return rc; -} +#include "../lutil.h" extern int class_handle_ioctl(unsigned int cmd, unsigned long arg); -int liblustre_ioctl(int dev_id, int opc, void *ptr) +static int liblustre_ioctl(int dev_id, unsigned int opc, void *ptr) { int rc = -EINVAL; @@ -76,15 +51,6 @@ int liblustre_ioctl(int dev_id, int opc, void *ptr) return rc; } -static void generate_random_uuid(unsigned char uuid_out[16]) -{ - int *arr = (int*)uuid_out; - int i; - - for (i = 0; i < sizeof(uuid_out)/sizeof(int); i++) - arr[i] = rand(); -} - static char *echo_server_nid = NULL; static char *echo_server_ostname = "obd1"; static char *osc_dev_name = "OSC_DEV_NAME"; @@ -259,18 +225,14 @@ int main(int argc, char **argv) return 1; } - srand(time(NULL)); - - tcpnal_mynid = rand(); -#if 1 portal_debug = 0; portal_subsystem_debug = 0; -#endif - if (init_current(argc, argv) || + liblustre_init_random(); + + if (liblustre_init_current(argv[0]) || init_obdclass() || init_lib_portals() || ptlrpc_init() || - ldlm_init() || mdc_init() || lov_init() || osc_init() || diff --git a/lustre/liblustre/tests/recovery_small.c b/lustre/liblustre/tests/recovery_small.c index 5aed06c..6cd9ba4 100644 --- a/lustre/liblustre/tests/recovery_small.c +++ b/lustre/liblustre/tests/recovery_small.c @@ -39,6 +39,8 @@ #include "test_common.h" +#define MAX_STRING_SIZE 2048 + static struct { const char *name; unsigned long code; @@ -52,6 +54,7 @@ static struct { static int drop_index = 0; static char mds_server[1024] = {0, }; +static char ssh_cmd[MAX_STRING_SIZE] = {0,}; int do_stat(const char *name, struct stat *buf) { @@ -121,14 +124,14 @@ void cleanup_dir(const char *path) #define FAIL() \ do { \ - char cmd[1024]; \ + char cmd[MAX_STRING_SIZE]; \ int rc; \ \ if (drop_arr[drop_index].name) { \ printf("server drops next %s\n", drop_arr[drop_index].name); \ sprintf(cmd, \ - "ssh %s \"echo %lu > /proc/sys/lustre/fail_loc\"", \ - mds_server, drop_arr[drop_index].code); \ + "%s %s \"echo %lu > /proc/sys/lustre/fail_loc\"", \ + ssh_cmd, mds_server, drop_arr[drop_index].code); \ if (system(cmd)) { \ printf("error excuting remote command: %d\n", rc); \ exit(rc); \ @@ -141,8 +144,8 @@ void cleanup_dir(const char *path) char cmd[1024]; \ \ if (drop_arr[drop_index].name) { \ - sprintf(cmd, "ssh %s \"echo 0 > /proc/sys/lustre/fail_loc\"", \ - mds_server); \ + sprintf(cmd, "%s %s \"echo 0 > /proc/sys/lustre/fail_loc\"", \ + ssh_cmd, mds_server); \ system(cmd); \ } \ } while (0) @@ -313,6 +316,7 @@ int main(int argc, char * argv[]) static struct option long_opts[] = { {"target", 1, 0, 0}, {"dumpfile", 1, 0, 0}, + {"ssh", 1, 0, 0}, {0, 0, 0, 0} }; @@ -329,12 +333,14 @@ int main(int argc, char * argv[]) setenv(ENV_LUSTRE_MNTTGT, optarg, 1); } else if (!strcmp(long_opts[opt_index].name, "dumpfile")) { setenv(ENV_LUSTRE_DUMPFILE, optarg, 1); + } else if (!strcmp(long_opts[opt_index].name, "ssh")) { + safe_strncpy(ssh_cmd, optarg, MAX_STRING_SIZE); } else usage(argv[0]); break; } case 's': - strcpy(mds_server, optarg); + safe_strncpy(mds_server, optarg, MAX_STRING_SIZE); break; default: usage(argv[0]); @@ -347,13 +353,18 @@ int main(int argc, char * argv[]) if (strlen(mds_server) == 0) usage(argv[0]); - sprintf(cmd, "ssh %s cat /dev/null", mds_server); + /* default to using ssh */ + if (!strlen(ssh_cmd)) { + safe_strncpy(ssh_cmd, "ssh", MAX_STRING_SIZE); + } + + sprintf(cmd, "%s %s cat /dev/null", ssh_cmd, mds_server); if (system(cmd)) { - printf("can't access server node: %s\n", mds_server); + printf("Can't access server node: %s using method: %s\n", mds_server, ssh_cmd); exit(-1); } - setenv(ENV_LUSTRE_TIMEOUT, "10", 1); + setenv(ENV_LUSTRE_TIMEOUT, "5", 1); __liblustre_setup_(); @@ -362,7 +373,9 @@ int main(int argc, char * argv[]) t2(); t3(); t4(); +#if 0 t5(); +#endif t6(); t7(); diff --git a/lustre/liblustre/tests/replay_single.c b/lustre/liblustre/tests/replay_single.c index 6645056..9628354 100644 --- a/lustre/liblustre/tests/replay_single.c +++ b/lustre/liblustre/tests/replay_single.c @@ -39,13 +39,14 @@ #include "test_common.h" +#define MAX_STRING_SIZE 2048 - -static char mds_server[1024] = {0,}; -static char barrier_script[1024] = {0,}; -static char failover_script[1024] = {0,}; -static char barrier_cmd[1024] = {0,}; -static char failover_cmd[1024] = {0,}; +static char mds_server[MAX_STRING_SIZE] = {0,}; +static char barrier_script[MAX_STRING_SIZE] = {0,}; +static char failover_script[MAX_STRING_SIZE] = {0,}; +static char barrier_cmd[MAX_STRING_SIZE] = {0,}; +static char failover_cmd[MAX_STRING_SIZE] = {0,}; +static char ssh_cmd[MAX_STRING_SIZE] = {0,}; static void replay_barrier() { @@ -91,9 +92,11 @@ static void mds_failover() void t0() { + char *path="/mnt/lustre/f0"; ENTRY("empty replay"); replay_barrier(); mds_failover(); + t_check_stat_fail("/mnt/lustre/f0"); LEAVE(); } @@ -315,19 +318,19 @@ extern void __liblustre_cleanup_(void); void usage(const char *cmd) { printf("Usage: \t%s --target mdsnid:/mdsname/profile -s mds_hostname " - "-b \"barrier cmd\" -f \"failover cmd\"\n", cmd); + "-b \"barrier cmd\" -f \"failover cmd\" [--rsh \"rsh_cmd\"]\n", cmd); printf(" \t%s --dumpfile dumpfile -s mds_hostname -b \"barrier cmd\" " - "-f \"failover cmd\"\n", cmd); + "-f \"failover cmd\" [--rsh \"rsh_cmd\"]\n", cmd); exit(-1); } void test_ssh() { - char cmd[1024]; + char cmd[MAX_STRING_SIZE]; - sprintf(cmd, "ssh %s cat /dev/null", mds_server); + sprintf(cmd, "%s %s cat /dev/null", ssh_cmd, mds_server); if (system(cmd)) { - printf("ssh can't access server node: %s\n", mds_server); + printf("Can't access server node: %s using method: %s\n", mds_server, ssh_cmd); exit(-1); } } @@ -338,6 +341,7 @@ int main(int argc, char * const argv[]) static struct option long_opts[] = { {"target", 1, 0, 0}, {"dumpfile", 1, 0, 0}, + {"ssh", 1, 0, 0}, {0, 0, 0, 0} }; @@ -354,18 +358,20 @@ int main(int argc, char * const argv[]) setenv(ENV_LUSTRE_MNTTGT, optarg, 1); } else if (!strcmp(long_opts[opt_index].name, "dumpfile")) { setenv(ENV_LUSTRE_DUMPFILE, optarg, 1); + } else if (!strcmp(long_opts[opt_index].name, "ssh")) { + safe_strncpy(ssh_cmd, optarg, MAX_STRING_SIZE); } else usage(argv[0]); break; } case 's': - strcpy(mds_server, optarg); + safe_strncpy(mds_server, optarg, MAX_STRING_SIZE); break; case 'b': - strcpy(barrier_script, optarg); + safe_strncpy(barrier_script, optarg, MAX_STRING_SIZE); break; case 'f': - strcpy(failover_script, optarg); + safe_strncpy(failover_script, optarg, MAX_STRING_SIZE); break; default: usage(argv[0]); @@ -378,11 +384,18 @@ int main(int argc, char * const argv[]) !strlen(failover_script)) usage(argv[0]); + /* default to using ssh */ + if (!strlen(ssh_cmd)) { + safe_strncpy(ssh_cmd, "ssh", MAX_STRING_SIZE); + } + test_ssh(); /* prepare remote command */ - sprintf(barrier_cmd, "ssh %s \"%s\"", mds_server, barrier_script); - sprintf(failover_cmd, "ssh %s \"%s\"", mds_server, failover_script); + sprintf(barrier_cmd, "%s %s \"%s\"", + ssh_cmd, mds_server, barrier_script); + sprintf(failover_cmd, "%s %s \"%s\"", + ssh_cmd, mds_server, failover_script); setenv(ENV_LUSTRE_TIMEOUT, "10", 1); diff --git a/lustre/liblustre/tests/sanity.c b/lustre/liblustre/tests/sanity.c index 944ae9c..acea41e 100644 --- a/lustre/liblustre/tests/sanity.c +++ b/lustre/liblustre/tests/sanity.c @@ -33,17 +33,20 @@ #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); \ @@ -55,15 +58,27 @@ #define LEAVE() \ do { \ - printf("----- END TEST successfully ---"); \ - printf("-----------------------------"); \ - printf("-------------------\n"); \ + char buf[100]; \ + int len; \ + sprintf(buf, "===== END TEST %s: successfully ", \ + __FUNCTION__); \ + len = strlen(buf); \ + if (len < 79) { \ + memset(buf+len, '=', 100-len); \ + buf[79] = '\n'; \ + buf[80] = 0; \ + } \ + printf("%s", buf); \ } while (0) +#define MAX_PATH_LENGTH 4096 + void t1() { - char *path="/mnt/lustre/test_t1"; + char path[MAX_PATH_LENGTH] = ""; + ENTRY("create/delete"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t1", lustre_path); t_touch(path); t_unlink(path); @@ -72,8 +87,10 @@ void t1() void t2() { - char *path="/mnt/lustre/test_t2"; + char path[MAX_PATH_LENGTH] = ""; + ENTRY("mkdir/rmdir"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t2", lustre_path); t_mkdir(path); t_rmdir(path); @@ -82,8 +99,10 @@ void t2() void t3() { - char *path="/mnt/lustre/test_t3"; + char path[MAX_PATH_LENGTH] = ""; + ENTRY("regular stat"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t3", lustre_path); t_touch(path); t_check_stat(path, NULL); @@ -93,8 +112,10 @@ void t3() void t4() { - char *path="/mnt/lustre/test_t4"; + char path[MAX_PATH_LENGTH] = ""; + ENTRY("dir stat"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t4", lustre_path); t_mkdir(path); t_check_stat(path, NULL); @@ -102,115 +123,54 @@ void t4() LEAVE(); } -#define PAGE_SIZE (4096) -#define _npages (2048) - -static int _buffer[_npages][PAGE_SIZE/sizeof(int)]; - -/* pos: i/o start from - * xfer: npages per transfer - */ -static void pages_io(int xfer, loff_t pos) +void t6() { - char *path="/mnt/lustre/test_t5"; - int check_sum[_npages] = {0,}; - int fd, rc, i, j; - - memset(_buffer, 0, sizeof(_buffer)); - - /* create sample data */ - for (i = 0; i < _npages; i++) { - for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { - _buffer[i][j] = rand(); - } - } + char path[MAX_PATH_LENGTH] = ""; + char path2[MAX_PATH_LENGTH] = ""; - /* compute checksum */ - for (i = 0; i < _npages; i++) { - for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { - check_sum[i] += _buffer[i][j]; - } - } + ENTRY("symlink"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t6", lustre_path); + snprintf(path2, MAX_PATH_LENGTH, "%s/test_t6_link", lustre_path); t_touch(path); - - fd = t_open(path); - - /* write */ - lseek(fd, pos, SEEK_SET); - for (i = 0; i < _npages; i += xfer) { - rc = write(fd, _buffer[i], PAGE_SIZE * xfer); - if (rc != PAGE_SIZE * xfer) { - printf("write error %d (i = %d)\n", rc, i); - exit(1); - } - } - printf("succefully write %d pages(%d per xfer)\n", _npages, xfer); - memset(_buffer, 0, sizeof(_buffer)); - - /* read */ - lseek(fd, pos, SEEK_SET); - for (i = 0; i < _npages; i += xfer) { - rc = read(fd, _buffer[i], PAGE_SIZE * xfer); - if (rc != PAGE_SIZE * xfer) { - printf("read error %d (i = %d)\n", rc, i); - exit(1); - } - } - printf("succefully read %d pages(%d per xfer)\n", _npages, xfer); - - /* compute checksum */ - for (i = 0; i < _npages; i++) { - int sum = 0; - for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { - sum += _buffer[i][j]; - } - if (sum != check_sum[i]) { - printf("chunk %d checksum error: expected 0x%x, get 0x%x\n", - i, check_sum[i], sum); - } - } - printf("checksum verified OK!\n"); - - t_close(fd); + t_symlink(path, path2); + t_check_stat(path2, NULL); + t_unlink(path2); t_unlink(path); + LEAVE(); } -void t5() +void t6b() { - char text[256]; - loff_t off_array[] = {1, 4, 17, 255, 258, 4095, 4097, 8191, 1024*1024*1024}; - int np = 1, i; - loff_t offset = 0; + char path[MAX_PATH_LENGTH] = ""; + char path2[MAX_PATH_LENGTH] = ""; + char cwd[MAX_PATH_LENGTH] = ""; + char *tmp; + int fd; - while (np <= _npages) { - sprintf(text, "pages_io: %d per transfer, offset %lld", - np, offset); - ENTRY(text); - pages_io(np, offset); - LEAVE(); - np += np; - } + ENTRY("symlink + chdir and open"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t6b", lustre_path); + snprintf(path2, MAX_PATH_LENGTH, "%s/test_t6b_link", lustre_path); - for (i = 0; i < sizeof(off_array)/sizeof(loff_t); i++) { - offset = off_array[i]; - sprintf(text, "pages_io: 16 per transfer, offset %lld", - offset); - ENTRY(text); - pages_io(16, offset); + t_mkdir(path); + t_symlink(path, path2); + t_check_stat(path2, NULL); + + tmp = getcwd(cwd, MAX_PATH_LENGTH); + if (tmp == NULL) { + fprintf(stderr, "current path too long to fit in " + "MAX_PATH_LENGTH?\n"); LEAVE(); + return; } -} + t_chdir(path2); + t_chdir(cwd); + t_rmdir(path); + t_touch(path); -void t6() -{ - char *path="/mnt/lustre/test_t6"; - char *path2="/mnt/lustre/test_t6_link"; - ENTRY("symlink"); + fd = t_open(path2); + t_close(fd); - t_touch(path); - t_symlink(path, path2); - t_check_stat(path2, NULL); t_unlink(path2); t_unlink(path); LEAVE(); @@ -218,19 +178,32 @@ void t6() void t7() { - char *path="/mnt/lustre/test_t7"; + char path[MAX_PATH_LENGTH] = ""; + int rc; + ENTRY("mknod"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t7", lustre_path); - t_mknod(path, S_IFCHR | 0644, 5, 4); - t_check_stat(path, NULL); - t_unlink(path); + if (geteuid() != 0) { + rc = mknod(path, S_IFCHR | 0644, (5<<8 | 4)); + if (rc != -1 || errno != EPERM) { + printf("mknod shouldn't success: rc %d, errno %d\n", + rc, errno); + } + } else { + t_mknod(path, S_IFCHR | 0644, 5, 4); + t_check_stat(path, NULL); + t_unlink(path); + } LEAVE(); } void t8() { - char *path="/mnt/lustre/test_t8"; + char path[MAX_PATH_LENGTH] = ""; + ENTRY("chmod"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t8", lustre_path); t_touch(path); t_chmod_raw(path, 0700); @@ -241,9 +214,12 @@ void t8() void t9() { - char *path="/mnt/lustre/test_t9"; - char *path2="/mnt/lustre/test_t9_link"; + char path[MAX_PATH_LENGTH] = ""; + char path2[MAX_PATH_LENGTH] = ""; + ENTRY("hard link"); + snprintf(path, MAX_PATH_LENGTH, "%s/test_t9", lustre_path); + snprintf(path2, MAX_PATH_LENGTH, "%s/test_t9_link", lustre_path); t_touch(path); t_link(path, path2); @@ -256,14 +232,22 @@ void t9() void t10() { - char *dir1="/mnt/lustre/test_t10_dir1"; - char *dir2="/mnt/lustre/test_t10_dir2"; - char *path1="/mnt/lustre/test_t10_reg1"; - char *path2="/mnt/lustre/test_t10_reg2"; - char *rename1="/mnt/lustre/test_t10_dir1/rename1"; - char *rename2="/mnt/lustre/test_t10_dir2/rename2"; - char *rename3="/mnt/lustre/test_t10_dir2/rename3"; + char dir1[MAX_PATH_LENGTH] = ""; + char dir2[MAX_PATH_LENGTH] = ""; + char path1[MAX_PATH_LENGTH] = ""; + char path2[MAX_PATH_LENGTH] = ""; + char rename1[MAX_PATH_LENGTH] = ""; + char rename2[MAX_PATH_LENGTH] = ""; + char rename3[MAX_PATH_LENGTH] = ""; + ENTRY("rename"); + snprintf(dir1, MAX_PATH_LENGTH, "%s/test_t10_dir1", lustre_path); + snprintf(dir2, MAX_PATH_LENGTH, "%s/test_t10_dir2", lustre_path); + snprintf(path1, MAX_PATH_LENGTH, "%s/test_t10_reg1", lustre_path); + snprintf(path2, MAX_PATH_LENGTH, "%s/test_t10_reg2", lustre_path); + snprintf(rename1, MAX_PATH_LENGTH, "%s/test_t10_dir1/rename1", lustre_path); + snprintf(rename2, MAX_PATH_LENGTH, "%s/test_t10_dir2/rename2", lustre_path); + snprintf(rename3, MAX_PATH_LENGTH, "%s/test_t10_dir2/rename3", lustre_path); t_mkdir(dir1); t_mkdir(dir2); @@ -281,12 +265,12 @@ void t10() void t11() { - char *base="/mnt/lustre"; - char path[4096], path2[4096]; + char *base=lustre_path; + char path[MAX_PATH_LENGTH], path2[MAX_PATH_LENGTH]; int i, j, level = 5, nreg = 5; ENTRY("deep tree"); - strcpy(path, base); + safe_strncpy(path, base, MAX_PATH_LENGTH); for (i = 0; i < level; i++) { for (j = 0; j < nreg; j++) { @@ -299,7 +283,7 @@ void t11() } for (i = level; i > 0; i--) { - strcpy(path, base); + safe_strncpy(path, base, MAX_PATH_LENGTH); for (j = 1; j < i; j++) strcat(path, "/dir"); @@ -317,10 +301,11 @@ void t11() void t12() { - char *dir="/mnt/lustre/test_t12_dir"; + char dir[MAX_PATH_LENGTH] = ""; char buf[1024*128]; int fd; ENTRY("empty directory readdir"); + snprintf(dir, MAX_PATH_LENGTH, "%s/test_t12_dir", lustre_path); t_mkdir(dir); fd = t_opendir(dir); @@ -332,13 +317,14 @@ void t12() void t13() { - char *dir="/mnt/lustre/test_t13_dir/"; + char dir[MAX_PATH_LENGTH] = ""; char name[1024]; char buf[1024]; const int nfiles = 20; char *prefix = "test13_filename_prefix_"; int fd, i; ENTRY("multiple entries directory readdir"); + snprintf(dir, MAX_PATH_LENGTH, "%s/test_t13_dir/", lustre_path); t_mkdir(dir); printf("Creating %d files...\n", nfiles); @@ -360,13 +346,16 @@ void t13() void t14() { - char *dir="/mnt/lustre/test_t14_dir/"; + char dir[MAX_PATH_LENGTH] = ""; char name[1024]; char buf[1024]; const int nfiles = 256; char *prefix = "test14_filename_long_prefix_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA___"; - int fd, i; + struct dirent64 *ent; + int fd, i, rc, pos, index; + loff_t base = 0; ENTRY(">1 block(4k) directory readdir"); + snprintf(dir, MAX_PATH_LENGTH, "%s/test_t14_dir/", lustre_path); t_mkdir(dir); printf("Creating %d files...\n", nfiles); @@ -375,7 +364,35 @@ void t14() t_touch(name); } fd = t_opendir(dir); - t_ls(fd, buf, sizeof(buf)); + printf("Listing...\n"); + index = 0; + while ((rc = getdirentries64(fd, buf, 1024, &base)) > 0) { + pos = 0; + while (pos < rc) { + char *item; + + ent = (struct dirent64 *) ((char*) buf + pos); + item = (char *) ent->d_name; + if (!strcmp(item, ".") || !strcmp(item, "..")) + goto iter; + if (strstr(item, prefix) != item) { + printf("found bad name %s\n", item); + exit(-1); + } + printf("[%03d]: %s\n", + index++, item + strlen(prefix)); +iter: + pos += ent->d_reclen; + } + } + if (rc < 0) { + printf("getdents error %d\n", rc); + exit(-1); + } + if (index != nfiles) { + printf("get %d files != %d\n", index, nfiles); + exit(-1); + } t_close(fd); printf("Cleanup...\n"); for (i = 0; i < nfiles; i++) { @@ -388,9 +405,10 @@ void t14() void t15() { - char *file = "/mnt/lustre/test_t15_file"; + char file[MAX_PATH_LENGTH] = ""; int fd; ENTRY("open-stat-close"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t15_file", lustre_path); t_touch(file); fd = t_open(file); @@ -400,6 +418,431 @@ void t15() LEAVE(); } +void t16() +{ + char file[MAX_PATH_LENGTH] = ""; + ENTRY("small-write-read"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t16_file", lustre_path); + + t_echo_create(file, "aaaaaaaaaaaaaaaaaaaaaa"); + t_grep(file, "aaaaaaaaaaaaaaaaaaaaaa"); + t_unlink(file); + LEAVE(); +} + +void t17() +{ + char file[MAX_PATH_LENGTH] = ""; + int fd; + ENTRY("open-unlink without close"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t17_file", lustre_path); + + fd = open(file, O_WRONLY | O_CREAT, 0666); + if (fd < 0) { + printf("failed to create file: %s\n", strerror(errno)); + exit(-1); + } + t_unlink(file); + LEAVE(); +} + +void t18() +{ + char file[MAX_PATH_LENGTH] = ""; + char buf[128]; + int fd, i; + struct stat statbuf[3]; + ENTRY("write should change mtime/atime"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t18_file", lustre_path); + + for (i = 0; i < 3; i++) { + fd = open(file, O_RDWR|O_CREAT|O_APPEND, (mode_t)0666); + if (fd < 0) { + printf("error open file: %s\n", strerror(errno)); + exit(-1); + } + if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { + printf("error write file\n"); + exit(-1); + } + close(fd); + if(stat(file, &statbuf[i]) != 0) { + printf("Error stat\n"); + exit(1); + } + printf("atime %lu, mtime %lu\n", + statbuf[i].st_atime, statbuf[i].st_mtime); + sleep(2); + } + + for (i = 1; i < 3; i++) { + if ((statbuf[i].st_atime <= statbuf[i-1].st_atime) || + (statbuf[i].st_mtime <= statbuf[i-1].st_mtime)) { + printf("time error\n"); + exit(-1); + } + } + t_unlink(file); + LEAVE(); +} + +void t18b() +{ + char file[MAX_PATH_LENGTH] = ""; + char buf[128]; + int fd, i; + struct stat statbuf[3]; + ENTRY("utime should change mtime/atime/ctime"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t23_file", lustre_path); + t_touch(file); + + for (i = 0; i < 3; i++) { + t_utime(file, NULL); + if(stat(file, &statbuf[i]) != 0) { + printf("Error stat\n"); + exit(1); + } + printf("atime %lu, mtime %lu, ctime %lu\n", + statbuf[i].st_atime, statbuf[i].st_mtime, + statbuf[i].st_ctime); + sleep(2); + } + + for (i = 1; i < 3; i++) { + if ((statbuf[i].st_atime <= statbuf[i-1].st_atime) || + (statbuf[i].st_mtime <= statbuf[i-1].st_mtime) || + (statbuf[i].st_ctime <= statbuf[i-1].st_ctime)) { + printf("time error\n"); + exit(-1); + } + } + t_unlink(file); + LEAVE(); +} + +void t19() +{ + char file[MAX_PATH_LENGTH] = ""; + int fd; + struct stat statbuf; + ENTRY("open(O_TRUNC) should trancate file to 0-length"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t19_file", lustre_path); + + t_echo_create(file, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + fd = open(file, O_RDWR|O_CREAT|O_TRUNC, (mode_t)0666); + if (fd < 0) { + printf("error open file: %s\n", strerror(errno)); + exit(-1); + } + close(fd); + if(stat(file, &statbuf) != 0) { + printf("Error stat\n"); + exit(1); + } + if (statbuf.st_size != 0) { + printf("size %ld is not zero\n", statbuf.st_size); + exit(-1); + } + t_unlink(file); + LEAVE(); +} + +void t20() +{ + char file[MAX_PATH_LENGTH] = ""; + int fd; + struct iovec iov[2]; + char buf[100]; + ssize_t ret; + ENTRY("trap app's general bad pointer for file i/o"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t20_file", lustre_path); + + fd = open(file, O_RDWR|O_CREAT, (mode_t)0666); + if (fd < 0) { + printf("error open file: %s\n", strerror(errno)); + exit(-1); + } + + ret = write(fd, NULL, 20); + if (ret != -1 || errno != EFAULT) { + printf("write 1: ret %ld, errno %d\n", ret, errno); + exit(1); + } + ret = write(fd, (void *)-1, 20); + if (ret != -1 || errno != EFAULT) { + printf("write 2: ret %ld, errno %d\n", ret, errno); + exit(1); + } + iov[0].iov_base = NULL; + iov[0].iov_len = 10; + iov[1].iov_base = (void *)-1; + iov[1].iov_len = 10; + ret = writev(fd, iov, 2); + if (ret != -1 || errno != EFAULT) { + printf("writev 1: ret %ld, errno %d\n", ret, errno); + exit(1); + } + iov[0].iov_base = NULL; + iov[0].iov_len = 0; + iov[1].iov_base = buf; + iov[1].iov_len = sizeof(buf); + ret = writev(fd, iov, 2); + if (ret != sizeof(buf)) { + printf("write 3 ret %ld, error %d\n", ret, errno); + exit(1); + } + lseek(fd, 0, SEEK_SET); + + ret = read(fd, NULL, 20); + if (ret != -1 || errno != EFAULT) { + printf("read 1: ret %ld, errno %d\n", ret, errno); + exit(1); + } + ret = read(fd, (void *)-1, 20); + if (ret != -1 || errno != EFAULT) { + printf("read 2: ret %ld, errno %d\n", ret, errno); + exit(1); + } + iov[0].iov_base = NULL; + iov[0].iov_len = 10; + iov[1].iov_base = (void *)-1; + iov[1].iov_len = 10; + ret = readv(fd, iov, 2); + if (ret != -1 || errno != EFAULT) { + printf("readv 1: ret %ld, errno %d\n", ret, errno); + exit(1); + } + iov[0].iov_base = NULL; + iov[0].iov_len = 0; + iov[1].iov_base = buf; + iov[1].iov_len = sizeof(buf); + ret = readv(fd, iov, 2); + if (ret != sizeof(buf)) { + printf("read 3 ret %ld, error %d\n", ret, errno); + exit(1); + } + + close(fd); + t_unlink(file); + LEAVE(); +} + +void t21() +{ + char file[MAX_PATH_LENGTH] = ""; + int fd, ret; + struct flock lock = { + .l_type = F_RDLCK, + .l_whence = SEEK_SET, + }; + + ENTRY("basic fcntl support"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t21_file", lustre_path); + + fd = open(file, O_RDWR|O_CREAT, (mode_t)0666); + if (fd < 0) { + printf("error open file: %m\n", file); + exit(-1); + } + + t_fcntl(fd, F_SETFL, O_APPEND); + if (!(ret = t_fcntl(fd, F_GETFL)) & O_APPEND) { + printf("error get flag: ret %x\n", ret); + exit(-1); + } + + t_fcntl(fd, F_SETLK, &lock); + t_fcntl(fd, F_GETLK, &lock); + lock.l_type = F_WRLCK; + t_fcntl(fd, F_SETLKW, &lock); + t_fcntl(fd, F_GETLK, &lock); + lock.l_type = F_UNLCK; + t_fcntl(fd, F_SETLK, &lock); + + close(fd); + t_unlink(file); + LEAVE(); +} + +void t22() +{ + char file[MAX_PATH_LENGTH] = ""; + int fd; + char *str = "1234567890"; + char buf[100]; + ssize_t ret; + ENTRY("make sure O_APPEND take effect"); + snprintf(file, MAX_PATH_LENGTH, "%s/test_t22_file", lustre_path); + + fd = open(file, O_RDWR|O_CREAT|O_APPEND, (mode_t)0666); + if (fd < 0) { + printf("error open file: %s\n", strerror(errno)); + exit(-1); + } + + lseek(fd, 100, SEEK_SET); + ret = write(fd, str, strlen(str)); + if (ret != strlen(str)) { + printf("write 1: ret %ld, errno %d\n", ret, errno); + exit(1); + } + + lseek(fd, 0, SEEK_SET); + ret = read(fd, buf, sizeof(buf)); + if (ret != strlen(str)) { + printf("read 1 got %ld\n", ret); + exit(1); + } + + if (memcmp(buf, str, strlen(str))) { + printf("read 1 data err\n"); + exit(1); + } + + if (fcntl(fd, F_SETFL, 0)) { + printf("fcntl err: %s\n", strerror(errno)); + exit(1); + } + + lseek(fd, 100, SEEK_SET); + ret = write(fd, str, strlen(str)); + if (ret != strlen(str)) { + printf("write 2: ret %ld, errno %d\n", ret, errno); + exit(1); + } + + lseek(fd, 100, SEEK_SET); + ret = read(fd, buf, sizeof(buf)); + if (ret != strlen(str)) { + printf("read 2 got %ld\n", ret); + exit(1); + } + + if (memcmp(buf, str, strlen(str))) { + printf("read 2 data err\n"); + exit(1); + } + + close(fd); + t_unlink(file); + LEAVE(); +} + +#define PAGE_SIZE (4096) +#define _npages (2048) + +static int _buffer[_npages][PAGE_SIZE/sizeof(int)]; + +/* pos: i/o start from + * xfer: npages per transfer + */ +static void pages_io(int xfer, loff_t pos) +{ + char path[MAX_PATH_LENGTH] = ""; + + int check_sum[_npages] = {0,}; + int fd, rc, i, j, data_error = 0; + struct timeval tw1, tw2, tr1, tr2; + double tw, tr; + + snprintf(path, MAX_PATH_LENGTH, "%s/test_t50", lustre_path); + memset(_buffer, 0, sizeof(_buffer)); + + /* create sample data */ + for (i = 0; i < _npages; i++) { + for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { + _buffer[i][j] = rand(); + } + } + + /* compute checksum */ + for (i = 0; i < _npages; i++) { + for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { + check_sum[i] += _buffer[i][j]; + } + } + + t_touch(path); + + fd = t_open(path); + + /* write */ + lseek(fd, pos, SEEK_SET); + gettimeofday(&tw1, NULL); + for (i = 0; i < _npages; i += xfer) { + rc = write(fd, _buffer[i], PAGE_SIZE * xfer); + if (rc != PAGE_SIZE * xfer) { + printf("write error %d (i = %d)\n", rc, i); + exit(1); + } + } + gettimeofday(&tw2, NULL); + + memset(_buffer, 0, sizeof(_buffer)); + + /* read */ + lseek(fd, pos, SEEK_SET); + gettimeofday(&tr1, NULL); + for (i = 0; i < _npages; i += xfer) { + rc = read(fd, _buffer[i], PAGE_SIZE * xfer); + if (rc != PAGE_SIZE * xfer) { + printf("read error %d (i = %d)\n", rc, i); + exit(1); + } + } + gettimeofday(&tr2, NULL); + + /* compute checksum */ + for (i = 0; i < _npages; i++) { + int sum = 0; + for (j = 0; j < PAGE_SIZE/sizeof(int); j++) { + sum += _buffer[i][j]; + } + if (sum != check_sum[i]) { + data_error = 1; + printf("chunk %d checksum error: expected 0x%x, get 0x%x\n", + i, check_sum[i], sum); + } + } + + t_close(fd); + t_unlink(path); + tw = (tw2.tv_sec - tw1.tv_sec) * 1000000 + (tw2.tv_usec - tw1.tv_usec); + tr = (tr2.tv_sec - tr1.tv_sec) * 1000000 + (tr2.tv_usec - tr1.tv_usec); + printf(" (R:%.3fM/s, W:%.3fM/s)\n", + (_npages * PAGE_SIZE) / (tw / 1000000.0) / (1024 * 1024), + (_npages * PAGE_SIZE) / (tr / 1000000.0) / (1024 * 1024)); + + if (data_error) + exit(1); +} + +void t50() +{ + loff_t off_array[] = {1, 17, 255, 258, 4095, 4097, 8191, + 1024*1024*1024*1024ULL}; + int np = 1, i; + loff_t offset = 0; + + ENTRY("4k aligned i/o sanity"); + while (np <= _npages) { + printf("%3d per xfer(total %d)...\t", np, _npages); + pages_io(np, offset); + np += np; + } + LEAVE(); + + ENTRY("4k un-aligned i/o sanity"); + for (i = 0; i < sizeof(off_array)/sizeof(loff_t); i++) { + offset = off_array[i]; + printf("16 per xfer(total %d), offset %10lld...\t", + _npages, offset); + pages_io(16, offset); + } + LEAVE(); +} + extern void __liblustre_setup_(void); extern void __liblustre_cleanup_(void); @@ -446,13 +889,12 @@ int main(int argc, char * const argv[]) __liblustre_setup_(); -#ifndef __CYGWIN__ t1(); t2(); t3(); t4(); - t5(); t6(); + t6b(); t7(); t8(); t9(); @@ -462,7 +904,15 @@ int main(int argc, char * const argv[]) t13(); t14(); t15(); -#endif + t16(); + t17(); + t18(); + t18b(); + t19(); + t20(); + t21(); + t22(); + t50(); printf("liblustre is about shutdown\n"); __liblustre_cleanup_(); diff --git a/lustre/liblustre/tests/test_common.c b/lustre/liblustre/tests/test_common.c index a87f0fa..03d005d 100644 --- a/lustre/liblustre/tests/test_common.c +++ b/lustre/liblustre/tests/test_common.c @@ -1,3 +1,7 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + */ + #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> @@ -7,6 +11,8 @@ #include <string.h> #include <errno.h> #include <dirent.h> +#include <utime.h> +#include <stdarg.h> #include "test_common.h" @@ -89,7 +95,7 @@ void t_mkdir(const char *path) { int rc; - rc = mkdir(path, 00644); + rc = mkdir(path, 00755); if (rc < 0) { printf("mkdir(%s) error: %s\n", path, strerror(errno)); EXIT(1); @@ -181,6 +187,27 @@ int t_open(const char *path) return fd; } +int t_chdir(const char *path) +{ + int rc = chdir(path); + if (rc < 0) { + printf("chdir(%s) error: %s\n", path, strerror(errno)); + EXIT_RET(rc); + } + return rc; +} + +int t_utime(const char *path, const struct utimbuf *buf) +{ + int rc = utime(path, buf); + if (rc < 0) { + printf("utime(%s, %p) error: %s\n", path, buf, + strerror(errno)); + EXIT_RET(rc); + } + return rc; +} + int t_opendir(const char *path) { int fd; @@ -209,6 +236,8 @@ int t_check_stat(const char *name, struct stat *buf) struct stat stat; int rc; + memset(&stat, 0, sizeof(stat)); + rc = lstat(name, &stat); if (rc) { printf("error %d stat %s\n", rc, name); @@ -216,6 +245,10 @@ int t_check_stat(const char *name, struct stat *buf) } if (buf) memcpy(buf, &stat, sizeof(*buf)); + if (stat.st_blksize == 0) { + printf("error: blksize is 0\n"); + EXIT_RET(-EINVAL); + } return 0; } @@ -313,3 +346,74 @@ void t_ls(int fd, char *buf, int size) EXIT(-1); } } + +int t_fcntl(int fd, int cmd, ...) +{ + va_list ap; + long arg; + struct flock *lock; + int rc = -1; + + va_start(ap, cmd); + switch (cmd) { + case F_GETFL: + va_end(ap); + rc = fcntl(fd, cmd); + if (rc == -1) { + printf("fcntl GETFL failed: %s\n", + strerror(errno)); + EXIT(1); + } + break; + case F_SETFL: + arg = va_arg(ap, long); + va_end(ap); + rc = fcntl(fd, cmd, arg); + if (rc == -1) { + printf("fcntl SETFL %ld failed: %s\n", + arg, strerror(errno)); + EXIT(1); + } + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + lock = va_arg(ap, struct flock *); + va_end(ap); + rc = fcntl(fd, cmd, lock); + if (rc == -1) { + printf("fcntl cmd %d failed: %s\n", + cmd, strerror(errno)); + EXIT(1); + } + break; + case F_DUPFD: + arg = va_arg(ap, long); + va_end(ap); + rc = fcntl(fd, cmd, arg); + if (rc == -1) { + printf("fcntl F_DUPFD %d failed: %s\n", + (int)arg, strerror(errno)); + EXIT(1); + } + break; + default: + va_end(ap); + printf("fcntl cmd %d not supported\n", cmd); + EXIT(1); + } + return rc; +} + +char *safe_strncpy(char *dst, char *src, int max_size) +{ + int src_size; + src_size=strlen(src); + if (src_size >= max_size) { + src_size=max_size-1; + } + memcpy(dst, src, src_size); + dst[src_size]=0; + + return(dst); +} diff --git a/lustre/liblustre/tests/test_common.h b/lustre/liblustre/tests/test_common.h index c3687b9..5949a42 100644 --- a/lustre/liblustre/tests/test_common.h +++ b/lustre/liblustre/tests/test_common.h @@ -8,6 +8,8 @@ extern int exit_on_err; +#include <utime.h> /* for utimbuf */ + void t_touch(const char *path); void t_create(const char *path); void t_link(const char *src, const char *dst); @@ -21,6 +23,8 @@ void t_chmod(const char *path, const char *format, ...); void t_rename(const char *oldpath, const char *newpath); int t_open_readonly(const char *path); int t_open(const char *path); +int t_chdir(const char *path); +int t_utime(const char *path, const struct utimbuf *buf); int t_opendir(const char *path); void t_close(int fd); int t_check_stat(const char *name, struct stat *buf); @@ -29,5 +33,8 @@ void t_echo_create(const char *path, const char *str); void t_grep(const char *path, char *str); void t_grep_v(const char *path, char *str); void t_ls(int fd, char *buf, int size); +int t_fcntl(int fd, int cmd, ...); + +char *safe_strncpy(char *dst, char *src, int max_size); #endif diff --git a/lustre/llite/autoMakefile.am b/lustre/llite/autoMakefile.am index 9fdde59..c389b31 100644 --- a/lustre/llite/autoMakefile.am +++ b/lustre/llite/autoMakefile.am @@ -8,4 +8,4 @@ modulefs_DATA = llite$(KMODEXT) endif DIST_SOURCES := $(llite-objs:.o=.c) llite_internal.h rw24.c super.c rw26.c super25.c -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ diff --git a/lustre/llite/dcache.c b/lustre/llite/dcache.c index 39a53bd..8f55aa4 100644 --- a/lustre/llite/dcache.c +++ b/lustre/llite/dcache.c @@ -167,10 +167,16 @@ restart: lustre_dump_dentry(dentry, 1); portals_debug_dumpstack(NULL); } else if (d_mountpoint(dentry)) { - CERROR("called on mountpoint (?) dentry=%p, inode=%p " - "ino=%lu\n", dentry, inode, inode->i_ino); - lustre_dump_dentry(dentry, 1); - portals_debug_dumpstack(NULL); + /* For mountpoints we skip removal of the dentry + which happens solely because we have a lock on it + obtained when this dentry was not a mountpoint yet */ + CDEBUG(D_DENTRY, "Skippind mountpoint dentry removal " + "%.*s (%p) parent %p\n", + dentry->d_name.len, + dentry->d_name.name, + dentry, dentry->d_parent); + + continue; } if (atomic_read(&dentry->d_count) == 0) { diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index d704cf0..a6e63bc 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -298,7 +298,7 @@ int ll_readdir(struct file * filp, void * dirent, filldir_t filldir) unsigned long n = pos >> PAGE_CACHE_SHIFT; unsigned long npages = dir_pages(inode); unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); - unsigned char *types = NULL; + unsigned char *types = ext2_filetype_table; int need_revalidate = (filp->f_version != inode->i_version); int rc = 0; ENTRY; @@ -341,16 +341,15 @@ int ll_readdir(struct file * filp, void * dirent, filldir_t filldir) for ( ;(char*)de <= limit; de = ext2_next_entry(de)) { if (de->inode) { int over; - unsigned char d_type = DT_UNKNOWN; rc = 0; /* no error if we return something */ - if (types && de->file_type < EXT2_FT_MAX) - d_type = types[de->file_type]; offset = (char *)de - kaddr; over = filldir(dirent, de->name, de->name_len, (n<<PAGE_CACHE_SHIFT) | offset, - le32_to_cpu(de->inode), d_type); + le32_to_cpu(de->inode), + types[de->file_type & + (EXT2_FT_MAX - 1)]); if (over) { ext2_put_page(page); GOTO(done, rc); @@ -398,7 +397,8 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), cmd=%#x\n", inode->i_ino, inode->i_generation, inode, cmd); - if (_IOC_TYPE(cmd) == 'T') /* tty ioctls */ + /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */ + if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */ return -ENOTTY; lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_IOCTL); @@ -421,7 +421,6 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, char *buf = NULL; char *filename; int namelen, rc, len = 0; - unsigned long valid; rc = obd_ioctl_getdata(&buf, &len, (void *)arg); if (rc) @@ -436,10 +435,9 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, GOTO(out, rc = -EINVAL); } - valid = OBD_MD_FLID; ll_inode2fid(&fid, inode); - rc = mdc_getattr_name(sbi->ll_mdc_exp, &fid, - filename, namelen, valid, 0, &request); + rc = mdc_getattr_name(sbi->ll_mdc_exp, &fid, filename, namelen, + OBD_MD_FLID, 0, &request); if (rc < 0) { CDEBUG(D_INFO, "mdc_getattr_name: %d\n", rc); GOTO(out, rc); @@ -498,13 +496,10 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, struct lov_mds_md *lmm; struct ll_fid fid; struct mds_body *body; - unsigned long valid = 0; int rc, lmmsize; - valid |= OBD_MD_FLDIREA; - ll_inode2fid(&fid, inode); - rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid, + rc = mdc_getattr(sbi->ll_mdc_exp, &fid, OBD_MD_FLDIREA, obd_size_diskmd(sbi->ll_osc_exp, NULL), &request); if (rc < 0) { @@ -840,8 +835,17 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, RETURN(rc?:error); } + case OBD_IOC_GETNAME: { + struct obd_device *obd = class_exp2obd(sbi->ll_osc_exp); + if (!obd) + RETURN(-EFAULT); + if (copy_to_user((void *)arg, obd->obd_name, + strlen(obd->obd_name) + 1)) + RETURN (-EFAULT); + RETURN(0); + } default: - return obd_iocontrol(cmd, sbi->ll_osc_exp,0,NULL,(void *)arg); + RETURN(obd_iocontrol(cmd, sbi->ll_osc_exp,0,NULL,(void *)arg)); } } diff --git a/lustre/llite/file.c b/lustre/llite/file.c index 66fdd1e..856079f 100644 --- a/lustre/llite/file.c +++ b/lustre/llite/file.c @@ -35,13 +35,21 @@ int ll_mdc_close(struct obd_export *mdc_exp, struct inode *inode, struct file *file) { - struct ll_file_data *fd = file->private_data; + struct ll_file_data *fd = LUSTRE_FPRIVATE(file); struct ptlrpc_request *req = NULL; struct obd_client_handle *och = &fd->fd_mds_och; struct obdo obdo; int rc; ENTRY; + /* clear group lock, if present */ + if (unlikely(fd->fd_flags & LL_FILE_GROUP_LOCKED)) { + struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd; + fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK); + rc = ll_extent_unlock(fd, inode, lsm, LCK_GROUP, + &fd->fd_cwlockh); + } + obdo.o_id = inode->i_ino; obdo.o_valid = OBD_MD_FLID; obdo_from_inode(&obdo, inode, OBD_MD_FLTYPE | OBD_MD_FLMODE | @@ -72,7 +80,7 @@ int ll_mdc_close(struct obd_export *mdc_exp, struct inode *inode, mdc_clear_open_replay_data(och); ptlrpc_req_finished(req); och->och_fh.cookie = DEAD_HANDLE_MAGIC; - file->private_data = NULL; + LUSTRE_FPRIVATE(file) = NULL; OBD_SLAB_FREE(fd, ll_file_data_slab, sizeof *fd); RETURN(rc); @@ -102,7 +110,7 @@ int ll_file_release(struct inode *inode, struct file *file) RETURN(0); lprocfs_counter_incr(sbi->ll_stats, LPROC_LL_RELEASE); - fd = (struct ll_file_data *)file->private_data; + fd = LUSTRE_FPRIVATE(file); LASSERT(fd != NULL); if (lsm) @@ -149,7 +157,7 @@ int ll_local_open(struct file *file, struct lookup_intent *it) LASSERT (body != NULL); /* reply already checked out */ LASSERT_REPSWABBED (req, 1); /* and swabbed down */ - LASSERT(!file->private_data); + LASSERT(!LUSTRE_FPRIVATE(file)); OBD_SLAB_ALLOC(fd, ll_file_data_slab, SLAB_KERNEL, sizeof *fd); /* We can't handle this well without reorganizing ll_file_open and @@ -158,7 +166,7 @@ int ll_local_open(struct file *file, struct lookup_intent *it) memcpy(&fd->fd_mds_och.och_fh, &body->handle, sizeof(body->handle)); fd->fd_mds_och.och_magic = OBD_CLIENT_HANDLE_MAGIC; - file->private_data = fd; + LUSTRE_FPRIVATE(file) = fd; ll_readahead_init(file->f_dentry->d_inode, &fd->fd_ras); lli->lli_io_epoch = body->io_epoch; @@ -376,7 +384,7 @@ void ll_pgcache_remove_extent(struct inode *inode, struct lov_stripe_md *lsm, CDEBUG(D_INODE|D_PAGE, "walking page indices start: %lu j: %lu " "count: %lu skip: %lu end: %lu%s\n", start, start % count, count, skip, end, discard ? " (DISCARDING)" : ""); - + /* walk through the vmas on the inode and tear down mmaped pages that * intersect with the lock. this stops immediately if there are no * mmap()ed regions of the file. This is not efficient at all and @@ -386,7 +394,7 @@ void ll_pgcache_remove_extent(struct inode *inode, struct lov_stripe_md *lsm, j = min(count - (i % count), end - i + 1); LASSERT(j > 0); LASSERT(inode->i_mapping); - if (ll_teardown_mmaps(inode->i_mapping, + if (ll_teardown_mmaps(inode->i_mapping, (__u64)i << PAGE_CACHE_SHIFT, ((__u64)(i+j) << PAGE_CACHE_SHIFT) - 1) ) break; @@ -639,10 +647,6 @@ static int ll_glimpse_callback(struct ldlm_lock *lock, void *reqp) return rc; } -__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms); -__u64 lov_merge_blocks(struct lov_stripe_md *lsm); -__u64 lov_merge_mtime(struct lov_stripe_md *lsm, __u64 current_time); - /* NB: lov_merge_size will prefer locally cached writes if they extend the * file (because it prefers KMS over RSS when larger) */ int ll_glimpse_size(struct inode *inode) @@ -752,10 +756,10 @@ int ll_extent_unlock(struct ll_file_data *fd, struct inode *inode, RETURN(rc); } -static ssize_t ll_file_read(struct file *filp, char *buf, size_t count, +static ssize_t ll_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - struct inode *inode = filp->f_dentry->d_inode; + struct inode *inode = file->f_dentry->d_inode; struct ll_inode_info *lli = ll_i2info(inode); struct lov_stripe_md *lsm = lli->lli_smd; struct ll_lock_tree tree; @@ -775,14 +779,36 @@ static ssize_t ll_file_read(struct file *filp, char *buf, size_t count, lprocfs_counter_add(ll_i2sbi(inode)->ll_stats, LPROC_LL_READ_BYTES, count); - if (!lsm) - RETURN(0); - - node = ll_node_from_inode(inode, *ppos, *ppos + count - 1, - LCK_PR); - tree.lt_fd = filp->private_data; - rc = ll_tree_lock(&tree, node, buf, count, - filp->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0); + if (!lsm) { + /* Read on file with no objects should return zero-filled + * buffers up to file size (we can get non-zero sizes with + * mknod + truncate, then opening file for read. This is a + * common pattern in NFS case, it seems). Bug 6243 */ + int notzeroed; + /* Since there are no objects on OSTs, we have nothing to get + * lock on and so we are forced to access inode->i_size + * unguarded */ + + /* Read beyond end of file */ + if (*ppos >= inode->i_size) + RETURN(0); + + if (count > inode->i_size - *ppos) + count = inode->i_size - *ppos; + /* Make sure to correctly adjust the file pos pointer for + * EFAULT case */ + notzeroed = clear_user(buf, count); + count -= notzeroed; + *ppos += count; + if (!count) + RETURN(-EFAULT); + RETURN(count); + } + + node = ll_node_from_inode(inode, *ppos, *ppos + count - 1, LCK_PR); + tree.lt_fd = LUSTRE_FPRIVATE(file); + rc = ll_tree_lock(&tree, node, buf, count, + file->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0); if (rc != 0) RETURN(rc); @@ -805,11 +831,11 @@ static ssize_t ll_file_read(struct file *filp, char *buf, size_t count, /* turn off the kernel's read-ahead */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) - filp->f_ramax = 0; + file->f_ramax = 0; #else - filp->f_ra.ra_pages = 0; + file->f_ra.ra_pages = 0; #endif - retval = generic_file_read(filp, buf, count, ppos); + retval = generic_file_read(file, buf, count, ppos); out: ll_tree_unlock(&tree); @@ -823,7 +849,6 @@ static ssize_t ll_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd; struct ll_lock_tree tree; struct ll_lock_tree_node *node; loff_t maxbytes = ll_file_maxbytes(inode); @@ -841,21 +866,23 @@ static ssize_t ll_file_write(struct file *file, const char *buf, size_t count, /* If file was opened for LL_IOC_LOV_SETSTRIPE but the ioctl wasn't * called on the file, don't fail the below assertion (bug 2388). */ - if (file->f_flags & O_LOV_DELAY_CREATE && lsm == NULL) + if (file->f_flags & O_LOV_DELAY_CREATE && + ll_i2info(inode)->lli_smd == NULL) RETURN(-EBADF); - LASSERT(lsm); - + LASSERT(ll_i2info(inode)->lli_smd != NULL); + if (file->f_flags & O_APPEND) node = ll_node_from_inode(inode, 0, OBD_OBJECT_EOF, LCK_PW); else node = ll_node_from_inode(inode, *ppos, *ppos + count - 1, LCK_PW); + if (IS_ERR(node)) RETURN(PTR_ERR(node)); - - tree.lt_fd = file->private_data; - rc = ll_tree_lock(&tree, node, buf, count, + + tree.lt_fd = LUSTRE_FPRIVATE(file); + rc = ll_tree_lock(&tree, node, buf, count, file->f_flags & O_NONBLOCK ? LDLM_FL_BLOCK_NOWAIT :0); if (rc != 0) RETURN(rc); @@ -1049,6 +1076,11 @@ static int ll_lov_setstripe(struct inode *inode, struct file *file, RETURN(-EFAULT); rc = ll_lov_setstripe_ea_info(inode, file, flags, &lum, sizeof(lum)); + if (rc == 0) { + put_user(0, &lump->lmm_stripe_count); + rc = obd_iocontrol(LL_IOC_LOV_GETSTRIPE, ll_i2obdexp(inode), + 0, ll_i2info(inode)->lli_smd, lump); + } RETURN(rc); } @@ -1063,17 +1095,78 @@ static int ll_lov_getstripe(struct inode *inode, unsigned long arg) (void *)arg); } +static int ll_get_grouplock(struct inode *inode, struct file *file, + unsigned long arg) +{ + struct ll_file_data *fd = file->private_data; + ldlm_policy_data_t policy = { .l_extent = { .start = 0, + .end = OBD_OBJECT_EOF}}; + struct lustre_handle lockh = { 0 }; + struct ll_inode_info *lli = ll_i2info(inode); + struct lov_stripe_md *lsm = lli->lli_smd; + int flags = 0, rc; + ENTRY; + + if (fd->fd_flags & LL_FILE_GROUP_LOCKED) { + RETURN(-EINVAL); + } + + policy.l_extent.gid = arg; + if (file->f_flags & O_NONBLOCK) + flags = LDLM_FL_BLOCK_NOWAIT; + + rc = ll_extent_lock(fd, inode, lsm, LCK_GROUP, &policy, &lockh, flags); + if (rc) + RETURN(rc); + + fd->fd_flags |= LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK; + fd->fd_gid = arg; + memcpy(&fd->fd_cwlockh, &lockh, sizeof(lockh)); + + RETURN(0); +} + +static int ll_put_grouplock(struct inode *inode, struct file *file, + unsigned long arg) +{ + struct ll_file_data *fd = file->private_data; + struct ll_inode_info *lli = ll_i2info(inode); + struct lov_stripe_md *lsm = lli->lli_smd; + int rc; + ENTRY; + + if (!(fd->fd_flags & LL_FILE_GROUP_LOCKED)) { + /* Ugh, it's already unlocked. */ + RETURN(-EINVAL); + } + + if (fd->fd_gid != arg) /* Ugh? Unlocking with different gid? */ + RETURN(-EINVAL); + + fd->fd_flags &= ~(LL_FILE_GROUP_LOCKED|LL_FILE_IGNORE_LOCK); + + rc = ll_extent_unlock(fd, inode, lsm, LCK_GROUP, &fd->fd_cwlockh); + if (rc) + RETURN(rc); + + fd->fd_gid = 0; + memset(&fd->fd_cwlockh, 0, sizeof(fd->fd_cwlockh)); + + RETURN(0); +} + int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct ll_file_data *fd = file->private_data; + struct ll_file_data *fd = LUSTRE_FPRIVATE(file); int flags; ENTRY; CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),cmd=%x\n", inode->i_ino, inode->i_generation, inode, cmd); - if (_IOC_TYPE(cmd) == 'T') /* tty ioctls */ + /* asm-ppc{,64} declares TCGETS, et. al. as type 't' not 'T' */ + if (_IOC_TYPE(cmd) == 'T' || _IOC_TYPE(cmd) == 't') /* tty ioctls */ RETURN(-ENOTTY); lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_IOCTL); @@ -1109,6 +1202,11 @@ int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd, case EXT3_IOC_GETVERSION_OLD: case EXT3_IOC_GETVERSION: RETURN(put_user(inode->i_generation, (int *) arg)); + case LL_IOC_GROUP_LOCK: + RETURN(ll_get_grouplock(inode, file, arg)); + case LL_IOC_GROUP_UNLOCK: + RETURN(ll_put_grouplock(inode, file, arg)); + /* We need to special case any other ioctls we want to handle, * to send them to the MDS/OST as appropriate and to properly * network encode the arg field. @@ -1124,24 +1222,28 @@ int ll_file_ioctl(struct inode *inode, struct file *file, unsigned int cmd, loff_t ll_file_seek(struct file *file, loff_t offset, int origin) { struct inode *inode = file->f_dentry->d_inode; - struct ll_file_data *fd = file->private_data; + struct ll_file_data *fd = LUSTRE_FPRIVATE(file); struct lov_stripe_md *lsm = ll_i2info(inode)->lli_smd; struct lustre_handle lockh = {0}; loff_t retval; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), to=%llu(%s)\n", - inode->i_ino, inode->i_generation, inode, - offset + ((origin == 2) ? inode->i_size : - (origin == 1) ? file->f_pos : 0), + retval = offset + ((origin == 2) ? inode->i_size : + (origin == 1) ? file->f_pos : 0); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p), to=%Lu=%#Lx(%s)\n", + inode->i_ino, inode->i_generation, inode, retval, retval, origin == 2 ? "SEEK_END": origin == 1 ? "SEEK_CUR" : "SEEK_SET"); lprocfs_counter_incr(ll_i2sbi(inode)->ll_stats, LPROC_LL_LLSEEK); if (origin == 2) { /* SEEK_END */ ldlm_policy_data_t policy = { .l_extent = {0, OBD_OBJECT_EOF }}; struct ll_inode_info *lli = ll_i2info(inode); - int rc; + int nonblock = 0, rc; + + if (file->f_flags & O_NONBLOCK) + nonblock = LDLM_FL_BLOCK_NOWAIT; - rc = ll_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh,0); + rc = ll_extent_lock(fd, inode, lsm, LCK_PR, &policy, &lockh, + nonblock); if (rc != 0) RETURN(rc); @@ -1303,8 +1405,7 @@ int ll_file_flock(struct file *file, int cmd, struct file_lock *file_lock) flags, mode, flock.l_flock.start, flock.l_flock.end); obddev = sbi->ll_mdc_exp->exp_obd; - rc = ldlm_cli_enqueue(obddev->obd_self_export, NULL, - obddev->obd_namespace, + rc = ldlm_cli_enqueue(sbi->ll_mdc_exp, NULL, obddev->obd_namespace, res_id, LDLM_FLOCK, &flock, mode, &flags, NULL, ldlm_flock_completion_ast, NULL, file_lock, NULL, 0, NULL, &lockh); @@ -1368,7 +1469,7 @@ int ll_inode_revalidate_it(struct dentry *dentry, struct lookup_intent *it) struct ptlrpc_request *req = NULL; struct ll_sb_info *sbi = ll_i2sbi(dentry->d_inode); struct ll_fid fid; - unsigned long valid = 0; + unsigned long valid = OBD_MD_FLGETATTR; int ealen = 0; if (S_ISREG(inode->i_mode)) { diff --git a/lustre/llite/llite_internal.h b/lustre/llite/llite_internal.h index 7e19c00..dab0979 100644 --- a/lustre/llite/llite_internal.h +++ b/lustre/llite/llite_internal.h @@ -6,6 +6,7 @@ #define LLITE_INTERNAL_H #include <linux/lustre_debug.h> +#include <linux/lustre_version.h> /* struct lustre_intent_data { @@ -17,6 +18,13 @@ struct lustre_intent_data { #define LL_IT2STR(it) ((it) ? ldlm_it2str((it)->it_op) : "0") +#if (LUSTRE_KERNEL_VERSION < 46) +#define LUSTRE_FPRIVATE(file) ((file)->private_data) +#else +#define LUSTRE_FPRIVATE(file) ((file)->fs_private) +#endif + + static inline struct lookup_intent *ll_nd2it(struct nameidata *nd) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) @@ -41,6 +49,7 @@ extern struct file_operations ll_pgcache_seq_fops; #define LLI_INODE_DEAD 0xdeadd00d #define LLI_F_HAVE_OST_SIZE_LOCK 0 #define LLI_F_HAVE_MDS_SIZE_LOCK 1 + struct ll_inode_info { int lli_inode_magic; int lli_size_pid; @@ -83,7 +92,6 @@ static inline struct ll_inode_info *ll_i2info(struct inode *inode) #endif } - /* default to about 40meg of readahead on a given system. That much tied * up in 512k readahead requests serviced at 40ms each is about 1GB/s. */ #define SBI_DEFAULT_READAHEAD_MAX (40UL << (20 - PAGE_CACHE_SHIFT)) @@ -92,6 +100,7 @@ enum ra_stat { RA_STAT_MISS, RA_STAT_DISTANT_READPAGE, RA_STAT_MISS_IN_WINDOW, + RA_STAT_FAILED_GRAB_PAGE, RA_STAT_FAILED_MATCH, RA_STAT_DISCARDED, RA_STAT_ZERO_LEN, @@ -107,6 +116,10 @@ struct ll_ra_info { unsigned long ra_stats[_NR_RA_STAT]; }; +/* flags for sbi->ll_flags */ +#define LL_SBI_NOLCK 0x1 /* DLM locking disabled (directio-only) */ +#define LL_SBI_CHECKSUM 0x2 /* checksum each page as it's written */ + struct ll_sb_info { struct list_head ll_list; /* this protects pglist and ra_info. It isn't safe to @@ -119,7 +132,6 @@ struct ll_sb_info { obd_id ll_rootino; /* number of root inode */ struct lustre_mount_data *ll_lmd; - char *ll_instance; int ll_flags; struct list_head ll_conn_chain; /* per-conn chain of SBs */ @@ -152,13 +164,14 @@ struct ll_file_data { struct obd_client_handle fd_mds_och; struct ll_readahead_state fd_ras; __u32 fd_flags; + struct lustre_handle fd_cwlockh; + unsigned long fd_gid; }; struct lov_stripe_md; extern spinlock_t inode_lock; -extern void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi); extern struct proc_dir_entry *proc_lustre_fs_root; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) @@ -206,17 +219,19 @@ extern kmem_cache_t *ll_async_page_slab; extern size_t ll_async_page_slab_size; struct ll_async_page { int llap_magic; - void *llap_cookie; - struct page *llap_page; - struct list_head llap_pending_write; /* only trust these if the page lock is providing exclusion */ unsigned int llap_write_queued:1, llap_defer_uptodate:1, llap_origin:3, llap_ra_used:1; + void *llap_cookie; + struct page *llap_page; + struct list_head llap_pending_write; struct list_head llap_pglist_item; /* user credit information for oss enforcement quota */ - struct obd_ucred llap_ouc; + struct lvfs_ucred llap_ouc; + /* checksum for paranoid I/O debugging */ + __u32 llap_checksum; }; enum { @@ -238,9 +253,16 @@ extern char *llap_origins[]; #endif /* llite/lproc_llite.c */ +#ifdef LPROCFS int lprocfs_register_mountpoint(struct proc_dir_entry *parent, struct super_block *sb, char *osc, char *mdc); void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi); +#else +static inline int lprocfs_register_mountpoint(struct proc_dir_entry *parent, + struct super_block *sb, char *osc, char *mdc){return 0;} +static inline void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi) {} +#endif + /* llite/dir.c */ extern struct file_operations ll_dir_operations; @@ -395,8 +417,6 @@ int ll_tree_lock(struct ll_lock_tree *tree, int ll_tree_unlock(struct ll_lock_tree *tree); -#define LL_SBI_NOLCK 0x1 - #define LL_MAX_BLKSIZE (4UL * 1024 * 1024) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) diff --git a/lustre/llite/llite_lib.c b/lustre/llite/llite_lib.c index cdc2d84..a55f822 100644 --- a/lustre/llite/llite_lib.c +++ b/lustre/llite/llite_lib.c @@ -230,7 +230,7 @@ int lustre_common_fill_super(struct super_block *sb, char *mdc, char *osc) /* make root inode * XXX: move this to after cbd setup? */ err = mdc_getattr(sbi->ll_mdc_exp, &rootfid, - OBD_MD_FLNOTOBD|OBD_MD_FLBLOCKS, 0, &request); + OBD_MD_FLGETATTR | OBD_MD_FLBLOCKS, 0, &request); if (err) { CERROR("mdc_getattr failed for root: rc = %d\n", err); GOTO(out_osc, err); @@ -249,7 +249,8 @@ int lustre_common_fill_super(struct super_block *sb, char *mdc, char *osc) ptlrpc_req_finished(request); if (root == NULL || is_bad_inode(root)) { - /* XXX might need iput() for bad inode */ + if (md.lsm != NULL) + obd_free_memmd(sbi->ll_osc_exp, &md.lsm); CERROR("lustre_lite: bad iget4 for root\n"); GOTO(out_root, err = -EBADF); } @@ -314,11 +315,11 @@ void lustre_dump_dentry(struct dentry *dentry, int recur) subdirs++; CERROR("dentry %p dump: name=%.*s parent=%.*s (%p), inode=%p, count=%u," - " flags=0x%x, vfs_flags=0x%x, fsdata=%p, %d subdirs\n", dentry, + " flags=0x%x, fsdata=%p, %d subdirs\n", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, dentry->d_parent, dentry->d_inode, atomic_read(&dentry->d_count), - dentry->d_flags, dentry->d_flags, dentry->d_fsdata, subdirs); + dentry->d_flags, dentry->d_fsdata, subdirs); if (dentry->d_inode != NULL) ll_dump_inode(dentry->d_inode); @@ -667,7 +668,7 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent) if (lmd->lmd_profile) { struct lustre_profile *lprof; struct config_llog_instance cfg; - int len; + char ll_instance[sizeof(sb) * 2 + 1]; if (lmd->lmd_mds[0] == '\0') { CERROR("no mds name\n"); @@ -681,13 +682,9 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent) /* generate a string unique to this super, let's try the address of the super itself.*/ - len = (sizeof(sb) * 2) + 1; - OBD_ALLOC(sbi->ll_instance, len); - if (sbi->ll_instance == NULL) - GOTO(out_free, err = -ENOMEM); - sprintf(sbi->ll_instance, "%p", sb); + sprintf(ll_instance, "%p", sb); - cfg.cfg_instance = sbi->ll_instance; + cfg.cfg_instance = ll_instance; cfg.cfg_uuid = sbi->ll_sb_uuid; err = lustre_process_log(lmd, lmd->lmd_profile, &cfg, 0); if (err < 0) { @@ -704,14 +701,14 @@ int lustre_fill_super(struct super_block *sb, void *data, int silent) if (osc) OBD_FREE(osc, strlen(osc) + 1); OBD_ALLOC(osc, strlen(lprof->lp_osc) + - strlen(sbi->ll_instance) + 2); - sprintf(osc, "%s-%s", lprof->lp_osc, sbi->ll_instance); + strlen(ll_instance) + 2); + sprintf(osc, "%s-%s", lprof->lp_osc, ll_instance); if (mdc) OBD_FREE(mdc, strlen(mdc) + 1); OBD_ALLOC(mdc, strlen(lprof->lp_mdc) + - strlen(sbi->ll_instance) + 2); - sprintf(mdc, "%s-%s", lprof->lp_mdc, sbi->ll_instance); + strlen(ll_instance) + 2); + sprintf(mdc, "%s-%s", lprof->lp_mdc, ll_instance); } if (!osc) { @@ -739,27 +736,7 @@ out_dev: out_free: if (sbi->ll_lmd) { - int len = strlen(sbi->ll_lmd->lmd_profile) + sizeof("-clean")+1; - int err; - - if (sbi->ll_instance != NULL) { - char * cln_prof; - struct config_llog_instance cfg; - - cfg.cfg_instance = sbi->ll_instance; - cfg.cfg_uuid = sbi->ll_sb_uuid; - - OBD_ALLOC(cln_prof, len); - sprintf(cln_prof, "%s-clean", sbi->ll_lmd->lmd_profile); - - err = lustre_process_log(sbi->ll_lmd, cln_prof, &cfg,0); - if (err < 0) { - CERROR("Unable to process log: %s\n", cln_prof); - lustre_manual_cleanup(sbi); - } - OBD_FREE(cln_prof, len); - OBD_FREE(sbi->ll_instance, strlen(sbi->ll_instance)+ 1); - } + lustre_manual_cleanup(sbi); OBD_FREE(sbi->ll_lmd, sizeof(*sbi->ll_lmd)); } lustre_free_sbi(sb); @@ -783,38 +760,8 @@ void lustre_put_super(struct super_block *sb) lustre_common_put_super(sb); if (sbi->ll_lmd != NULL) { -#if 0 - char * cln_prof; - int len = strlen(sbi->ll_lmd->lmd_profile) + sizeof("-clean")+1; - int err; - struct config_llog_instance cfg; - - if (force_umount) { - CERROR("force umount, doing manual cleanup\n"); - lustre_manual_cleanup(sbi); - GOTO(free_lmd, 0); - } - - cfg.cfg_instance = sbi->ll_instance; - cfg.cfg_uuid = sbi->ll_sb_uuid; - - OBD_ALLOC(cln_prof, len); - sprintf(cln_prof, "%s-clean", sbi->ll_lmd->lmd_profile); - - err = lustre_process_log(sbi->ll_lmd, cln_prof, &cfg, 0); - if (err < 0) { - CERROR("Unable to process log: %s, doing manual cleanup" - "\n", cln_prof); - lustre_manual_cleanup(sbi); - } - - OBD_FREE(cln_prof, len); - free_lmd: -#else lustre_manual_cleanup(sbi); -#endif OBD_FREE(sbi->ll_lmd, sizeof(*sbi->ll_lmd)); - OBD_FREE(sbi->ll_instance, strlen(sbi->ll_instance) + 1); } lustre_free_sbi(sb); @@ -975,6 +922,10 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr) CDEBUG(D_INODE, "setting mtime %lu, ctime %lu, now = %lu\n", LTIME_S(attr->ia_mtime), LTIME_S(attr->ia_ctime), CURRENT_SECONDS); + + + /* NB: ATTR_SIZE will only be set after this point if the size + * resides on the MDS, ie, this file has no objects. */ if (lsm) attr->ia_valid &= ~ATTR_SIZE; @@ -1001,12 +952,11 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr) RETURN(rc); } - /* We call inode_setattr to adjust timestamps, but we first - * clear ATTR_SIZE to avoid invoking vmtruncate. - * - * NB: ATTR_SIZE will only be set at this point if the size - * resides on the MDS, ie, this file has no objects. */ - attr->ia_valid &= ~ATTR_SIZE; + /* We call inode_setattr to adjust timestamps. + * If there is at least some data in file, we cleared ATTR_SIZE + * above to avoid invoking vmtruncate, otherwise it is important + * to call vmtruncate in inode_setattr to update inode->i_size + * (bug 6196) */ inode_setattr(inode, attr); ll_update_inode(inode, md.body, md.lsm); @@ -1057,8 +1007,17 @@ int ll_setattr_raw(struct inode *inode, struct iattr *attr) if (attr->ia_size == 0) ast_flags = LDLM_AST_DISCARD_DATA; + up(&inode->i_sem); + UP_WRITE_I_ALLOC_SEM(inode); rc = ll_extent_lock(NULL, inode, lsm, LCK_PW, &policy, &lockh, ast_flags); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) + DOWN_WRITE_I_ALLOC_SEM(inode); + down(&inode->i_sem); +#else + down(&inode->i_sem); + DOWN_WRITE_I_ALLOC_SEM(inode); +#endif if (rc != 0) RETURN(rc); @@ -1208,6 +1167,9 @@ void ll_update_inode(struct inode *inode, struct mds_body *body, inode->i_blksize = min(lsm->lsm_xfersize, LL_MAX_BLKSIZE); if (lli->lli_smd != lsm) obd_free_memmd(ll_i2obdexp(inode), &lsm); + } else { + inode->i_blksize = max(inode->i_blksize, + inode->i_sb->s_blocksize); } if (body->valid & OBD_MD_FLID) @@ -1334,11 +1296,10 @@ int ll_iocontrol(struct inode *inode, struct file *file, switch(cmd) { case EXT3_IOC_GETFLAGS: { struct ll_fid fid; - unsigned long valid = OBD_MD_FLFLAGS; struct mds_body *body; ll_inode2fid(&fid, inode); - rc = mdc_getattr(sbi->ll_mdc_exp, &fid, valid, 0, &req); + rc = mdc_getattr(sbi->ll_mdc_exp, &fid, OBD_MD_FLFLAGS,0,&req); if (rc) { CERROR("failure %d inode %lu\n", rc, inode->i_ino); RETURN(-abs(rc)); @@ -1427,7 +1388,8 @@ void ll_umount_begin(struct super_block *sb) struct obd_device *obd; struct obd_ioctl_data ioc_data = { 0 }; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:\n"); + CDEBUG(D_VFSTRACE, "VFS Op: superblock %p count %d active %d\n", sb, + sb->s_count, atomic_read(&sb->s_active)); obd = class_exp2obd(sbi->ll_mdc_exp); if (obd == NULL) { @@ -1537,4 +1499,3 @@ struct ll_async_page *llite_pglist_next_llap(struct ll_sb_info *sbi, LBUG(); return NULL; } - diff --git a/lustre/llite/llite_mmap.c b/lustre/llite/llite_mmap.c index 995a7de..4be05bf 100644 --- a/lustre/llite/llite_mmap.c +++ b/lustre/llite/llite_mmap.c @@ -42,7 +42,6 @@ #include <linux/iobuf.h> #endif - #define DEBUG_SUBSYSTEM S_LLITE #include <linux/lustre_mds.h> @@ -68,7 +67,6 @@ struct ll_lock_tree_node { struct inode *lt_inode; }; -__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms); int lt_get_mmap_locks(struct ll_lock_tree *tree, unsigned long addr, size_t count); @@ -362,7 +360,7 @@ struct page *ll_nopage(struct vm_area_struct *vma, unsigned long address, #endif { struct file *filp = vma->vm_file; - struct ll_file_data *fd = filp->private_data; + struct ll_file_data *fd = LUSTRE_FPRIVATE(filp); struct inode *inode = filp->f_dentry->d_inode; struct lustre_handle lockh = { 0 }; ldlm_policy_data_t policy; diff --git a/lustre/llite/lproc_llite.c b/lustre/llite/lproc_llite.c index fed41c3..54cb257 100644 --- a/lustre/llite/lproc_llite.c +++ b/lustre/llite/lproc_llite.c @@ -29,20 +29,13 @@ #include "llite_internal.h" -/* /proc/lustre/llite mount point registration */ struct proc_dir_entry *proc_lustre_fs_root; + +#ifdef LPROCFS +/* /proc/lustre/llite mount point registration */ struct file_operations llite_dump_pgcache_fops; struct file_operations ll_ra_stats_fops; -#ifndef LPROCFS -int lprocfs_register_mountpoint(struct proc_dir_entry *parent, - struct super_block *sb, char *osc, char *mdc) -{ - return 0; -} -void lprocfs_unregister_mountpoint(struct ll_sb_info *sbi){} -#else - long long mnt_instance; static int ll_rd_blksize(char *page, char **start, off_t off, int count, @@ -261,6 +254,40 @@ static int ll_wr_max_cached_mb(struct file *file, const char *buffer, return count; } +static int ll_rd_checksum(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct super_block *sb = data; + struct ll_sb_info *sbi = ll_s2sbi(sb); + + return snprintf(page, count, "%u\n", + (sbi->ll_flags & LL_SBI_CHECKSUM) ? 1 : 0); +} + +static int ll_wr_checksum(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct super_block *sb = data; + struct ll_sb_info *sbi = ll_s2sbi(sb); + int val, rc; + + rc = lprocfs_write_helper(buffer, count, &val); + if (rc) + return rc; + + if (val) + sbi->ll_flags |= LL_SBI_CHECKSUM; + else + sbi->ll_flags &= ~LL_SBI_CHECKSUM; + + rc = obd_set_info(sbi->ll_osc_exp, strlen("checksum"), "checksum", + sizeof(val), &val); + if (rc) + CWARN("Failed to set OSC checksum flags: %d\n", rc); + + return count; +} + static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", ll_rd_sb_uuid, 0, 0 }, //{ "mntpt_path", ll_rd_path, 0, 0 }, @@ -275,6 +302,7 @@ static struct lprocfs_vars lprocfs_obd_vars[] = { { "max_read_ahead_mb", ll_rd_max_readahead_mb, ll_wr_max_readahead_mb, 0 }, { "max_cached_mb", ll_rd_max_cached_mb, ll_wr_max_cached_mb, 0 }, + { "checksum_pages", ll_rd_checksum, ll_wr_checksum, 0 }, { 0 } }; @@ -655,6 +683,7 @@ static int ll_ra_stats_seq_show(struct seq_file *seq, void *v) [RA_STAT_MISS] = "misses", [RA_STAT_DISTANT_READPAGE] = "readpage not consecutive", [RA_STAT_MISS_IN_WINDOW] = "miss inside window", + [RA_STAT_FAILED_GRAB_PAGE] = "failed grab_cache_page", [RA_STAT_FAILED_MATCH] = "failed lock match", [RA_STAT_DISCARDED] = "read but discarded", [RA_STAT_ZERO_LEN] = "zero length file", diff --git a/lustre/llite/namei.c b/lustre/llite/namei.c index 430eb17..48a166b 100644 --- a/lustre/llite/namei.c +++ b/lustre/llite/namei.c @@ -75,7 +75,8 @@ static int ll_test_inode(struct inode *inode, void *opaque) return 0; /* Apply the attributes in 'opaque' to this inode */ - ll_update_inode(inode, md->body, md->lsm); + if (!(inode->i_state & (I_FREEING | I_CLEAR))) + ll_update_inode(inode, md->body, md->lsm); return 1; } @@ -372,6 +373,8 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry, ll_inode2fid(&pfid, parent); ll_i2uctxt(&ctxt, parent, NULL); + it->it_create_mode &= ~current->fs->umask; + rc = mdc_intent_lock(ll_i2mdcexp(parent), &ctxt, &pfid, dentry->d_name.name, dentry->d_name.len, NULL, 0, NULL, it, lookup_flags, &req, ll_mdc_blocking_ast); @@ -684,11 +687,22 @@ static int ll_rmdir_raw(struct nameidata *nd) struct inode *dir = nd->dentry->d_inode; struct ptlrpc_request *request = NULL; struct mdc_op_data op_data; + struct dentry *dentry; int rc; ENTRY; CDEBUG(D_VFSTRACE, "VFS Op:name=%.*s,dir=%lu/%u(%p)\n", nd->last.len, nd->last.name, dir->i_ino, dir->i_generation, dir); + /* Check if we have something mounted at the dir we are going to delete + * In such a case there would always be dentry present. */ + dentry = d_lookup(nd->dentry, &nd->last); + if (dentry) { + int mounted = d_mountpoint(dentry); + dput(dentry); + if (mounted) + RETURN(-EBUSY); + } + ll_prepare_mdc_op_data(&op_data, dir, NULL, nd->last.name, nd->last.len, S_IFDIR); rc = mdc_unlink(ll_i2sbi(dir)->ll_mdc_exp, &op_data, &request); diff --git a/lustre/llite/rw.c b/lustre/llite/rw.c index 6ca5b8c..4a59682 100644 --- a/lustre/llite/rw.c +++ b/lustre/llite/rw.c @@ -101,8 +101,6 @@ static int ll_brw(int cmd, struct inode *inode, struct obdo *oa, RETURN(rc); } -__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms); - /* this isn't where truncate starts. roughly: * sys_truncate->ll_setattr_raw->vmtruncate->ll_truncate * we grab the lock back in setattr_raw to avoid races. @@ -115,8 +113,8 @@ void ll_truncate(struct inode *inode) struct obdo oa; int rc; ENTRY; - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) to %llu\n", inode->i_ino, - inode->i_generation, inode, inode->i_size); + CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p) to %Lu=%#Lx\n",inode->i_ino, + inode->i_generation, inode, inode->i_size, inode->i_size); if (lli->lli_size_pid != current->pid) { EXIT; @@ -131,14 +129,33 @@ void ll_truncate(struct inode *inode) LASSERT(atomic_read(&lli->lli_size_sem.count) <= 0); + /* XXX I'm pretty sure this is a hack to paper over a more fundamental + * race condition. */ if (lov_merge_size(lsm, 0) == inode->i_size) { - CDEBUG(D_VFSTRACE, "skipping punch for "LPX64" (size = %llu)\n", - lsm->lsm_object_id, inode->i_size); + CDEBUG(D_VFSTRACE, "skipping punch for obj "LPX64", %Lu=%#Lx\n", + lsm->lsm_object_id, inode->i_size, inode->i_size); GOTO(out_unlock, 0); } - CDEBUG(D_INFO, "calling punch for "LPX64" (new size %llu)\n", - lsm->lsm_object_id, inode->i_size); + if (unlikely((ll_i2sbi(inode)->ll_flags & LL_SBI_CHECKSUM) && + (inode->i_size & ~PAGE_MASK))) { + /* If the truncate leaves behind a partial page, update its + * checksum. */ + struct page *page = find_get_page(inode->i_mapping, + inode->i_size >> PAGE_CACHE_SHIFT); + if (page != NULL) { + struct ll_async_page *llap = llap_cast_private(page); + if (llap != NULL) { + llap->llap_checksum = + crc32_le(0, kmap(page), PAGE_SIZE); + kunmap(page); + } + page_cache_release(page); + } + } + + CDEBUG(D_INFO, "calling punch for "LPX64" (new size %Lu=%#Lx)\n", + lsm->lsm_object_id, inode->i_size, inode->i_size); oa.o_id = lsm->lsm_object_id; oa.o_valid = OBD_MD_FLID; @@ -166,7 +183,6 @@ void ll_truncate(struct inode *inode) up(&lli->lli_size_sem); } /* ll_truncate */ -__u64 lov_merge_size(struct lov_stripe_md *lsm, int kms); int ll_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) { @@ -241,24 +257,13 @@ int ll_prepare_write(struct file *file, struct page *page, unsigned from, return rc; } -struct ll_async_page *llap_from_cookie(void *cookie) -{ - struct ll_async_page *llap = cookie; - if (llap->llap_magic != LLAP_MAGIC) - return ERR_PTR(-EINVAL); - return llap; -}; - static int ll_ap_make_ready(void *data, int cmd) { struct ll_async_page *llap; struct page *page; ENTRY; - llap = llap_from_cookie(data); - if (IS_ERR(llap)) - RETURN(-EINVAL); - + llap = LLAP_FROM_COOKIE(data); page = llap->llap_page; LASSERT(cmd != OBD_BRW_READ); @@ -308,10 +313,7 @@ static int ll_ap_refresh_count(void *data, int cmd) /* readpage queues with _COUNT_STABLE, shouldn't get here. */ LASSERT(cmd != OBD_BRW_READ); - llap = llap_from_cookie(data); - if (IS_ERR(llap)) - RETURN(PTR_ERR(llap)); - + llap = LLAP_FROM_COOKIE(data); page = llap->llap_page; lli = ll_i2info(page->mapping->host); lsm = lli->lli_smd; @@ -345,6 +347,8 @@ void ll_inode_fill_obdo(struct inode *inode, int cmd, struct obdo *oa) oa->o_valid |= OBD_MD_FLIFID | OBD_MD_FLEPOCH; mdc_pack_fid(obdo_fid(oa), inode->i_ino, 0, inode->i_mode); oa->o_easize = ll_i2info(inode)->lli_io_epoch; + oa->o_uid = inode->i_uid; + oa->o_gid = inode->i_gid; valid_flags |= OBD_MD_FLMTIME | OBD_MD_FLCTIME | OBD_MD_FLUID | OBD_MD_FLGID; @@ -358,21 +362,16 @@ static void ll_ap_fill_obdo(void *data, int cmd, struct obdo *oa) struct ll_async_page *llap; ENTRY; - llap = llap_from_cookie(data); - if (IS_ERR(llap)) { - EXIT; - return; - } - + llap = LLAP_FROM_COOKIE(data); ll_inode_fill_obdo(llap->llap_page->mapping->host, cmd, oa); EXIT; } -static void ll_ap_get_ucred(void *data, struct obd_ucred *ouc) +static void ll_ap_get_ucred(void *data, struct lvfs_ucred *ouc) { struct ll_async_page *llap; - llap = llap_from_cookie(data); + llap = LLAP_FROM_COOKIE(data); if (IS_ERR(llap)) { EXIT; return; @@ -436,6 +435,7 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction) while (--total >= 0 && count < want) { struct page *page; + int keep; if (unlikely(need_resched())) { spin_unlock(&sbi->ll_lock); @@ -459,18 +459,24 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction) continue; } - /* If page is dirty or undergoing IO don't discard it */ if (llap->llap_write_queued || PageDirty(page) || (!PageUptodate(page) && - llap->llap_origin != LLAP_ORIGIN_READAHEAD)) { + llap->llap_origin != LLAP_ORIGIN_READAHEAD)) + keep = 1; + else + keep = 0; + + LL_CDEBUG_PAGE(D_PAGE, page,"%s LRU page: %s%s%s%s origin %s\n", + keep ? "keep" : "drop", + llap->llap_write_queued ? "wq " : "", + PageDirty(page) ? "pd " : "", + PageUptodate(page) ? "" : "!pu ", + llap->llap_defer_uptodate ? "" : "!du", + llap_origins[llap->llap_origin]); + + /* If page is dirty or undergoing IO don't discard it */ + if (keep) { unlock_page(page); - LL_CDEBUG_PAGE(D_PAGE, page, "can't drop from cache: " - "%s%s%s%s origin %s\n", - llap->llap_write_queued ? "wq " : "", - PageDirty(page) ? "pd " : "", - PageUptodate(page) ? "" : "!pu ", - llap->llap_defer_uptodate ? "" : "!du", - llap_origins[llap->llap_origin]); continue; } @@ -478,8 +484,6 @@ int llap_shrink_cache(struct ll_sb_info *sbi, int shrink_fraction) spin_unlock(&sbi->ll_lock); ++count; - LL_CDEBUG_PAGE(D_PAGE, page, "drop from cache %lu/%lu\n", - count, want); if (page->mapping != NULL) { ll_ra_accounting(page, page->mapping); ll_truncate_complete_page(page); @@ -557,7 +561,29 @@ struct ll_async_page *llap_from_page(struct page *page, unsigned origin) list_add_tail(&llap->llap_pglist_item, &sbi->ll_pglist); spin_unlock(&sbi->ll_lock); -out: + out: + if (unlikely(sbi->ll_flags & LL_SBI_CHECKSUM)) { + __u32 csum = 0; + csum = crc32_le(csum, kmap(page), PAGE_SIZE); + kunmap(page); + if (origin == LLAP_ORIGIN_READAHEAD || + origin == LLAP_ORIGIN_READPAGE) { + llap->llap_checksum = 0; + } else if (origin == LLAP_ORIGIN_COMMIT_WRITE || + llap->llap_checksum == 0) { + llap->llap_checksum = csum; + CDEBUG(D_PAGE, "page %p cksum %x\n", page, csum); + } else if (llap->llap_checksum == csum) { + /* origin == LLAP_ORIGIN_WRITEPAGE */ + CDEBUG(D_PAGE, "page %p cksum %x confirmed\n", + page, csum); + } else { + /* origin == LLAP_ORIGIN_WRITEPAGE */ + LL_CDEBUG_PAGE(D_ERROR, page, "old cksum %x != new " + "%x!\n", llap->llap_checksum, csum); + } + } + llap->llap_origin = origin; RETURN(llap); } @@ -568,6 +594,7 @@ static int queue_or_sync_write(struct obd_export *exp, struct inode *inode, { unsigned long size_index = inode->i_size >> PAGE_SHIFT; struct obd_io_group *oig; + struct ll_sb_info *sbi = ll_i2sbi(inode); int rc; ENTRY; @@ -603,6 +630,22 @@ static int queue_or_sync_write(struct obd_export *exp, struct inode *inode, to = size_to; } + /* compare the checksum once before the page leaves llite */ + if (unlikely((sbi->ll_flags & LL_SBI_CHECKSUM) && + llap->llap_checksum != 0)) { + __u32 csum = 0; + struct page *page = llap->llap_page; + csum = crc32_le(csum, kmap(page), PAGE_SIZE); + kunmap(page); + if (llap->llap_checksum == csum) { + CDEBUG(D_PAGE, "page %p cksum %x confirmed\n", + page, csum); + } else { + CERROR("page %p old cksum %x != new cksum %x!\n", + page, llap->llap_checksum, csum); + } + } + rc = obd_queue_group_io(exp, ll_i2info(inode)->lli_smd, NULL, oig, llap->llap_cookie, OBD_BRW_WRITE, 0, to, 0, ASYNC_READY | ASYNC_URGENT | @@ -661,11 +704,11 @@ int ll_commit_write(struct file *file, struct page *page, unsigned from, RETURN(-EINVAL); /* set user credit information for this page */ - llap->llap_ouc.ouc_fsuid = current->fsuid; - llap->llap_ouc.ouc_fsgid = current->fsgid; - llap->llap_ouc.ouc_cap = current->cap_effective; + llap->llap_ouc.luc_fsuid = current->fsuid; + llap->llap_ouc.luc_fsgid = current->fsgid; + llap->llap_ouc.luc_cap = current->cap_effective; ll_i2uctxt(&ctxt, inode, NULL); - llap->llap_ouc.ouc_suppgid1 = ctxt.gid1; + llap->llap_ouc.luc_suppgid1 = ctxt.gid1; /* queue a write for some time in the future the first time we * dirty the page */ @@ -736,12 +779,7 @@ void ll_ap_completion(void *data, int cmd, struct obdo *oa, int rc) struct page *page; ENTRY; - llap = llap_from_cookie(data); - if (IS_ERR(llap)) { - EXIT; - return; - } - + llap = LLAP_FROM_COOKIE(data); page = llap->llap_page; LASSERT(PageLocked(page)); @@ -840,7 +878,7 @@ void ll_removepage(struct page *page) EXIT; } -static int ll_page_matches(struct page *page, int readahead) +static int ll_page_matches(struct page *page, int fd_flags) { struct lustre_handle match_lockh = {0}; struct inode *inode = page->mapping->host; @@ -848,12 +886,15 @@ static int ll_page_matches(struct page *page, int readahead) int flags, matches; ENTRY; + if (unlikely(fd_flags & LL_FILE_GROUP_LOCKED)) + RETURN(1); + page_extent.l_extent.start = (__u64)page->index << PAGE_CACHE_SHIFT; page_extent.l_extent.end = page_extent.l_extent.start + PAGE_CACHE_SIZE - 1; - flags = LDLM_FL_TEST_LOCK; - if (!readahead) - flags |= LDLM_FL_CBPENDING | LDLM_FL_BLOCK_GRANTED; + flags = LDLM_FL_TEST_LOCK | LDLM_FL_BLOCK_GRANTED; + if (!(fd_flags & LL_FILE_READAHEAD)) + flags |= LDLM_FL_CBPENDING; matches = obd_match(ll_i2sbi(inode)->ll_osc_exp, ll_i2info(inode)->lli_smd, LDLM_EXTENT, &page_extent, LCK_PR | LCK_PW, &flags, inode, @@ -979,6 +1020,7 @@ static int ll_readahead(struct ll_readahead_state *ras, /* skip locked pages from previous readpage calls */ page = grab_cache_page_nowait_gfp(mapping, i, gfp_mask); if (page == NULL) { + ll_ra_stats_inc(mapping, RA_STAT_FAILED_GRAB_PAGE); CDEBUG(D_READA, "g_c_p_n failed\n"); continue; } @@ -994,7 +1036,7 @@ static int ll_readahead(struct ll_readahead_state *ras, goto next_page; /* bail when we hit the end of the lock. */ - if ((rc = ll_page_matches(page, 1)) <= 0) { + if ((rc = ll_page_matches(page, flags|LL_FILE_READAHEAD)) <= 0){ LL_CDEBUG_PAGE(D_READA | D_PAGE, page, "lock match failed: rc %d\n", rc); ll_ra_stats_inc(mapping, RA_STAT_FAILED_MATCH); @@ -1157,8 +1199,8 @@ int ll_writepage(struct page *page) llap->llap_cookie, ASYNC_READY | ASYNC_URGENT); } else { - rc = queue_or_sync_write(exp, inode, llap, - PAGE_SIZE, ASYNC_READY | ASYNC_URGENT); + rc = queue_or_sync_write(exp, inode, llap, PAGE_SIZE, + ASYNC_READY | ASYNC_URGENT); } if (rc) page_cache_release(page); @@ -1184,7 +1226,7 @@ out: */ int ll_readpage(struct file *filp, struct page *page) { - struct ll_file_data *fd = filp->private_data; + struct ll_file_data *fd = LUSTRE_FPRIVATE(filp); struct inode *inode = page->mapping->host; struct obd_export *exp; struct ll_async_page *llap; @@ -1194,9 +1236,10 @@ int ll_readpage(struct file *filp, struct page *page) LASSERT(PageLocked(page)); LASSERT(!PageUptodate(page)); - CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),offset="LPX64"\n", + CDEBUG(D_VFSTRACE, "VFS Op:inode=%lu/%u(%p),offset=%Lu=%#Lx\n", inode->i_ino, inode->i_generation, inode, - (((obd_off)page->index) << PAGE_SHIFT)); + (((loff_t)page->index) << PAGE_SHIFT), + (((loff_t)page->index) << PAGE_SHIFT)); LASSERT(atomic_read(&filp->f_dentry->d_inode->i_count) > 0); rc = oig_init(&oig); @@ -1228,7 +1271,7 @@ int ll_readpage(struct file *filp, struct page *page) GOTO(out_oig, rc = 0); } - rc = ll_page_matches(page, 0); + rc = ll_page_matches(page, fd->fd_flags); if (rc < 0) { LL_CDEBUG_PAGE(D_ERROR, page, "lock match failed: rc %d\n", rc); GOTO(out, rc); diff --git a/lustre/llite/super.c b/lustre/llite/super.c index 78f9206..dbf1b20 100644 --- a/lustre/llite/super.c +++ b/lustre/llite/super.c @@ -102,8 +102,8 @@ static int __init init_lustre_lite(void) if (ll_file_data_slab == NULL) return -ENOMEM; - proc_lustre_fs_root = proc_lustre_root ? - proc_mkdir("llite", proc_lustre_root) : NULL; + if (proc_lustre_root) + proc_lustre_fs_root = proc_mkdir("llite", proc_lustre_root); ll_register_cache(&ll_cache_definition); diff --git a/lustre/lov/autoMakefile.am b/lustre/lov/autoMakefile.am index e6854ec..30b89f631 100644 --- a/lustre/lov/autoMakefile.am +++ b/lustre/lov/autoMakefile.am @@ -15,4 +15,4 @@ modulefs_DATA = lov$(KMODEXT) endif # MODULES DIST_SOURCES = $(lov-objs:.o=.c) lov_internal.h -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ diff --git a/lustre/lov/lov_internal.h b/lustre/lov/lov_internal.h index 3127d08..097e7c8 100644 --- a/lustre/lov/lov_internal.h +++ b/lustre/lov/lov_internal.h @@ -132,6 +132,7 @@ int lov_stripe_number(struct lov_stripe_md *lsm, obd_off lov_off); void qos_shrink_lsm(struct lov_request_set *set); int qos_prep_create(struct lov_obd *lov, struct lov_request_set *set, int newea); +int qos_remedy_create(struct lov_request_set *set, struct lov_request *req); /* lov_request.c */ void lov_set_add_req(struct lov_request *req, struct lov_request_set *set); @@ -220,4 +221,8 @@ int lov_getstripe(struct obd_export *exp, /* lproc_lov.c */ extern struct file_operations lov_proc_target_fops; +/* Quota stuff */ +int lov_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl); +int lov_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl); + #endif diff --git a/lustre/lov/lov_merge.c b/lustre/lov/lov_merge.c index 082dae3..30092ae 100644 --- a/lustre/lov/lov_merge.c +++ b/lustre/lov/lov_merge.c @@ -99,11 +99,14 @@ int lov_adjust_kms(struct obd_export *exp, struct lov_stripe_md *lsm, if (shrink) { struct lov_oinfo *loi; - int i = 0; - for (loi = lsm->lsm_oinfo; i < lsm->lsm_stripe_count; - i++, loi++) { - kms = lov_size_to_stripe(lsm, size, i); + for (loi = lsm->lsm_oinfo; stripe < lsm->lsm_stripe_count; + stripe++, loi++) { + kms = lov_size_to_stripe(lsm, size, stripe); loi->loi_kms = loi->loi_rss = kms; + CDEBUG(D_INODE, + "stripe %d KMS %sing "LPU64"->"LPU64"\n", + stripe, kms > loi->loi_kms ? "increas":"shrink", + loi->loi_kms, kms); } RETURN(0); } diff --git a/lustre/lov/lov_obd.c b/lustre/lov/lov_obd.c index 3f212f3..7104aa9 100644 --- a/lustre/lov/lov_obd.c +++ b/lustre/lov/lov_obd.c @@ -62,7 +62,9 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt, struct obd_device *tgt_obd; struct obd_uuid lov_osc_uuid = { "LOV_OSC_UUID" }; struct lustre_handle conn = {0, }; +#ifdef __KERNEL__ struct proc_dir_entry *lov_proc_dir; +#endif int rc; ENTRY; @@ -115,6 +117,7 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt, tgt->active = 1; lov->desc.ld_active_tgt_count++; +#ifdef __KERNEL__ lov_proc_dir = lprocfs_srch(obd->obd_proc_entry, "target_obds"); if (lov_proc_dir) { struct obd_device *osc_obd = class_conn2obd(&conn); @@ -138,6 +141,7 @@ static int lov_connect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt, lov_proc_dir = NULL; } } +#endif RETURN(0); } @@ -172,7 +176,7 @@ static int lov_connect(struct lustre_handle *conn, struct obd_device *obd, if (rc) GOTO(out_disc, rc); } - + class_export_put(exp); RETURN (0); @@ -200,11 +204,13 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt) struct lov_obd *lov = &obd->u.lov; int rc; ENTRY; - + + CDEBUG(D_CONFIG, "Disconnecting lov target %s\n", obd->obd_uuid.uuid); + lov_proc_dir = lprocfs_srch(obd->obd_proc_entry, "target_obds"); if (lov_proc_dir) { struct proc_dir_entry *osc_symlink; - + osc_symlink = lprocfs_srch(lov_proc_dir, osc_obd->obd_name); if (osc_symlink) { lprocfs_remove(osc_symlink); @@ -214,7 +220,7 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt) osc_obd->obd_name); } } - + if (obd->obd_no_recov) { /* Pass it on to our clients. * XXX This should be an argument to disconnect, @@ -224,7 +230,7 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt) osc_obd->obd_no_recov = 1; } - obd_register_observer(tgt->ltd_exp->exp_obd, NULL); + obd_register_observer(osc_obd, NULL); rc = obd_disconnect(tgt->ltd_exp); if (rc) { @@ -234,15 +240,15 @@ static int lov_disconnect_obd(struct obd_device *obd, struct lov_tgt_desc *tgt) } rc = 0; } - + if (tgt->active) { tgt->active = 0; lov->desc.ld_active_tgt_count--; } - + tgt->ltd_exp = NULL; RETURN(0); -} +} static int lov_disconnect(struct obd_export *exp) { @@ -252,21 +258,21 @@ static int lov_disconnect(struct obd_export *exp) int rc, i; ENTRY; + rc = class_disconnect(exp); + if (!lov->tgts) - goto out_local; + RETURN(rc); /* Only disconnect the underlying layers on the final disconnect. */ lov->refcount--; if (lov->refcount != 0) - goto out_local; + RETURN(rc); for (i = 0, tgt = lov->tgts; i < lov->desc.ld_tgt_count; i++, tgt++) { if (tgt->ltd_exp) lov_disconnect_obd(obd, tgt); } - out_local: - rc = class_disconnect(exp); RETURN(rc); } @@ -306,7 +312,7 @@ static int lov_set_osc_active(struct lov_obd *lov, struct obd_uuid *uuid, GOTO(out, rc); } - CDEBUG(D_INFO, "Marking OSC %s %sactive\n", uuid->uuid, + CDEBUG(D_INFO, "Marking OSC %s %sactive\n", uuid->uuid, activate ? "" : "in"); tgt->active = activate; @@ -336,7 +342,7 @@ static int lov_notify(struct obd_device *obd, struct obd_device *watched, uuid = &watched->u.cli.cl_import->imp_target_uuid; /* Set OSC as active before notifying the observer, so the - * observer can use the OSC normally. + * observer can use the OSC normally. */ rc = lov_set_osc_active(&obd->u.lov, uuid, active); if (rc) { @@ -352,104 +358,6 @@ static int lov_notify(struct obd_device *obd, struct obd_device *watched, RETURN(rc); } -static int lov_setup(struct obd_device *obd, obd_count len, void *buf) -{ - struct lprocfs_static_vars lvars; - struct lustre_cfg *lcfg = buf; - struct lov_desc *desc; - struct lov_obd *lov = &obd->u.lov; - int count; - ENTRY; - - if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) { - CERROR("LOV setup requires a descriptor\n"); - RETURN(-EINVAL); - } - - desc = (struct lov_desc *)lustre_cfg_buf(lcfg, 1); - - if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) { - CERROR("descriptor size wrong: %d > %d\n", - (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1)); - RETURN(-EINVAL); - } - - if (desc->ld_magic != LOV_DESC_MAGIC) { - if (desc->ld_magic == __swab32(LOV_DESC_MAGIC)) { - CDEBUG(D_OTHER, "%s: Swabbing lov desc %p\n", - obd->obd_name, desc); - lustre_swab_lov_desc(desc); - } else { - CERROR("%s: Bad lov desc magic: %#x\n", - obd->obd_name, desc->ld_magic); - RETURN(-EINVAL); - } - } - - if (desc->ld_default_stripe_size < PTLRPC_MAX_BRW_SIZE) { - CWARN("Increasing default_stripe_size "LPU64" to %u\n", - desc->ld_default_stripe_size, PTLRPC_MAX_BRW_SIZE); - CWARN("Please update config and run --write-conf on MDS\n"); - - desc->ld_default_stripe_size = PTLRPC_MAX_BRW_SIZE; - } - - /* Because of 64-bit divide/mod operations only work with a 32-bit - * divisor in a 32-bit kernel, we cannot support a stripe width - * of 4GB or larger on 32-bit CPUs. - */ - count = desc->ld_default_stripe_count; - if (count && (count * desc->ld_default_stripe_size) > ~0UL) { - CERROR("LOV: stripe width "LPU64"x%u > %lu on 32-bit system\n", - desc->ld_default_stripe_size, count, ~0UL); - RETURN(-EINVAL); - } - - /* Allocate space for target list */ - if (desc->ld_tgt_count) - count = desc->ld_tgt_count; - lov->bufsize = sizeof(struct lov_tgt_desc) * count; - OBD_ALLOC(lov->tgts, lov->bufsize); - if (lov->tgts == NULL) { - CERROR("Out of memory\n"); - RETURN(-EINVAL); - } - memset(lov->tgts, 0, lov->bufsize); - - desc->ld_active_tgt_count = 0; - lov->desc = *desc; - spin_lock_init(&lov->lov_lock); - - lprocfs_init_vars(lov, &lvars); - lprocfs_obd_setup(obd, lvars.obd_vars); -#ifdef __KERNEL__ - { - struct proc_dir_entry *entry; - - entry = create_proc_entry("target_obd", 0444, - obd->obd_proc_entry); - if (entry != NULL) { - entry->proc_fops = &lov_proc_target_fops; - entry->data = obd; - } - } -#endif - - RETURN(0); -} - -static int lov_cleanup(struct obd_device *obd) -{ - struct lov_obd *lov = &obd->u.lov; - - lprocfs_obd_cleanup(obd); - obd_llog_finish(obd, 0); - if (lov->tgts) - OBD_FREE(lov->tgts, lov->bufsize); - - RETURN(0); -} - static int lov_add_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen) { @@ -517,7 +425,7 @@ lov_add_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen) index, tgt->ltd_gen, lov->desc.ld_tgt_count); if (lov->refcount == 0) - /* lov_connect hasn't been called yet. So we'll do the + /* lov_connect hasn't been called yet. So we'll do the lov_connect_obd on this obd when that fn first runs. */ RETURN(0); @@ -575,7 +483,7 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen) RETURN(-EINVAL); } - tgt = lov->tgts + index; + tgt = &lov->tgts[index]; if (obd_uuid_empty(&tgt->uuid)) { CERROR("LOV target at index %d is not setup.\n", index); @@ -588,7 +496,7 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen) RETURN(-EINVAL); } - if (tgt->ltd_exp) + if (tgt->ltd_exp) lov_disconnect_obd(obd, tgt); /* XXX - right now there is a dependency on ld_tgt_count being the @@ -604,6 +512,134 @@ lov_del_obd(struct obd_device *obd, struct obd_uuid *uuidp, int index, int gen) RETURN(rc); } +static int lov_setup(struct obd_device *obd, obd_count len, void *buf) +{ + struct lprocfs_static_vars lvars; + struct lustre_cfg *lcfg = buf; + struct lov_desc *desc; + struct lov_obd *lov = &obd->u.lov; + int count; + ENTRY; + + if (LUSTRE_CFG_BUFLEN(lcfg, 1) < 1) { + CERROR("LOV setup requires a descriptor\n"); + RETURN(-EINVAL); + } + + desc = (struct lov_desc *)lustre_cfg_buf(lcfg, 1); + + if (sizeof(*desc) > LUSTRE_CFG_BUFLEN(lcfg, 1)) { + CERROR("descriptor size wrong: %d > %d\n", + (int)sizeof(*desc), LUSTRE_CFG_BUFLEN(lcfg, 1)); + RETURN(-EINVAL); + } + + if (desc->ld_magic != LOV_DESC_MAGIC) { + if (desc->ld_magic == __swab32(LOV_DESC_MAGIC)) { + CDEBUG(D_OTHER, "%s: Swabbing lov desc %p\n", + obd->obd_name, desc); + lustre_swab_lov_desc(desc); + } else { + CERROR("%s: Bad lov desc magic: %#x\n", + obd->obd_name, desc->ld_magic); + RETURN(-EINVAL); + } + } + + if (desc->ld_default_stripe_size < PTLRPC_MAX_BRW_SIZE) { + CWARN("Increasing default_stripe_size "LPU64" to %u\n", + desc->ld_default_stripe_size, PTLRPC_MAX_BRW_SIZE); + CWARN("Please update config and run --write-conf on MDS\n"); + + desc->ld_default_stripe_size = PTLRPC_MAX_BRW_SIZE; + } else if (desc->ld_default_stripe_size & (LOV_MIN_STRIPE_SIZE - 1)) { + CWARN("default_stripe_size "LPU64" isn't a multiple of %u\n", + desc->ld_default_stripe_size, LOV_MIN_STRIPE_SIZE); + CWARN("Please update config and run --write-conf on MDS\n"); + + desc->ld_default_stripe_size &= ~(LOV_MIN_STRIPE_SIZE - 1); + } + + if (desc->ld_default_stripe_count == 0) + desc->ld_default_stripe_count = 1; + + /* Because of 64-bit divide/mod operations only work with a 32-bit + * divisor in a 32-bit kernel, we cannot support a stripe width + * of 4GB or larger on 32-bit CPUs. */ + count = desc->ld_default_stripe_count; + if ((count ? count : desc->ld_tgt_count) * + desc->ld_default_stripe_size > ~0UL) { + CERROR("LOV: stripe width "LPU64"x%u > %lu on 32-bit system\n", + desc->ld_default_stripe_size, count, ~0UL); + RETURN(-EINVAL); + } + + /* Allocate space for target list */ + if (desc->ld_tgt_count) + count = desc->ld_tgt_count; + lov->bufsize = sizeof(struct lov_tgt_desc) * count; + OBD_ALLOC(lov->tgts, lov->bufsize); + if (lov->tgts == NULL) { + CERROR("Out of memory\n"); + RETURN(-EINVAL); + } + memset(lov->tgts, 0, lov->bufsize); + + desc->ld_active_tgt_count = 0; + lov->desc = *desc; + spin_lock_init(&lov->lov_lock); + + lprocfs_init_vars(lov, &lvars); + lprocfs_obd_setup(obd, lvars.obd_vars); +#ifdef LPROCFS + { + struct proc_dir_entry *entry; + + entry = create_proc_entry("target_obd", 0444, + obd->obd_proc_entry); + if (entry != NULL) { + entry->proc_fops = &lov_proc_target_fops; + entry->data = obd; + } + } +#endif + + RETURN(0); +} + +static int lov_precleanup(struct obd_device *obd, int stage) +{ + int rc = 0; + ENTRY; + + if (stage < 2) + RETURN(0); + + rc = obd_llog_finish(obd, 0); + if (rc != 0) + CERROR("failed to cleanup llogging subsystems\n"); + + RETURN(rc); +} + +static int lov_cleanup(struct obd_device *obd) +{ + struct lov_obd *lov = &obd->u.lov; + + lprocfs_obd_cleanup(obd); + if (lov->tgts) { + int i; + struct lov_tgt_desc *tgt; + for (i = 0, tgt = lov->tgts; + i < lov->desc.ld_tgt_count; i++, tgt++) { + if (!obd_uuid_empty(&tgt->uuid)) + lov_del_obd(obd, &tgt->uuid, i, 0); + } + OBD_FREE(lov->tgts, lov->bufsize); + } + RETURN(0); +} + static int lov_process_config(struct obd_device *obd, obd_count len, void *buf) { struct lustre_cfg *lcfg = buf; @@ -617,9 +653,9 @@ static int lov_process_config(struct obd_device *obd, obd_count len, void *buf) switch(cmd = lcfg->lcfg_command) { case LCFG_LOV_ADD_OBD: case LCFG_LOV_DEL_OBD: { - if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid)) + if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof(obd_uuid.uuid)) GOTO(out, rc = -EINVAL); - + obd_str2uuid(&obd_uuid, lustre_cfg_buf(lcfg, 1)); if (sscanf(lustre_cfg_buf(lcfg, 2), "%d", &index) != 1) @@ -688,6 +724,7 @@ static int lov_clear_orphans(struct obd_export *export, struct obdo *src_oa, memcpy(tmp_oa, src_oa, sizeof(*tmp_oa)); + LASSERT(lov->tgts[i].ltd_exp); /* XXX: LOV STACKING: use real "obj_mdp" sub-data */ err = obd_create(lov->tgts[i].ltd_exp, tmp_oa, &obj_mdp, oti); if (err) @@ -778,11 +815,11 @@ static int lov_create(struct obd_export *exp, struct obdo *src_oa, RETURN(rc); list_for_each (pos, &set->set_list) { - struct lov_request *req = + struct lov_request *req = list_entry(pos, struct lov_request, rq_link); /* XXX: LOV STACKING: use real "obj_mdp" sub-data */ - rc = obd_create(lov->tgts[req->rq_idx].ltd_exp, + rc = obd_create(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, &req->rq_md, oti); lov_update_create_set(set, req, rc); } @@ -790,11 +827,11 @@ static int lov_create(struct obd_export *exp, struct obdo *src_oa, RETURN(rc); } -#define ASSERT_LSM_MAGIC(lsmp) \ -do { \ - LASSERT((lsmp) != NULL); \ - LASSERTF((lsmp)->lsm_magic == LOV_MAGIC, "%p, %x", \ - (lsmp), (lsmp)->lsm_magic); \ +#define ASSERT_LSM_MAGIC(lsmp) \ +do { \ + LASSERT((lsmp) != NULL); \ + LASSERTF((lsmp)->lsm_magic == LOV_MAGIC, "%p->lsm_magic=%x\n", \ + (lsmp), (lsmp)->lsm_magic); \ } while (0) static int lov_destroy(struct obd_export *exp, struct obdo *oa, @@ -828,8 +865,8 @@ static int lov_destroy(struct obd_export *exp, struct obdo *oa, err = lov_update_common_set(set, req, rc); if (rc) { CERROR("error: destroying objid "LPX64" subobj " - LPX64" on OST idx %d: rc = %d\n", - set->set_oa->o_id, req->rq_oa->o_id, + LPX64" on OST idx %d: rc = %d\n", + set->set_oa->o_id, req->rq_oa->o_id, req->rq_idx, rc); if (!rc) rc = err; @@ -855,30 +892,30 @@ static int lov_getattr(struct obd_export *exp, struct obdo *oa, RETURN(-ENODEV); lov = &exp->exp_obd->u.lov; - + rc = lov_prep_getattr_set(exp, oa, lsm, &set); if (rc) RETURN(rc); list_for_each (pos, &set->set_list) { req = list_entry(pos, struct lov_request, rq_link); - + CDEBUG(D_INFO, "objid "LPX64"[%d] has subobj "LPX64" at idx " - "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, + "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, req->rq_idx); - rc = obd_getattr(lov->tgts[req->rq_idx].ltd_exp, + rc = obd_getattr(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, NULL); err = lov_update_common_set(set, req, rc); if (err) { CERROR("error: getattr objid "LPX64" subobj " LPX64" on OST idx %d: rc = %d\n", - set->set_oa->o_id, req->rq_oa->o_id, + set->set_oa->o_id, req->rq_oa->o_id, req->rq_idx, err); break; } } - + rc = lov_fini_getattr_set(set); if (err) rc = err; @@ -928,22 +965,22 @@ static int lov_getattr_async(struct obd_export *exp, struct obdo *oa, list_for_each (pos, &lovset->set_list) { req = list_entry(pos, struct lov_request, rq_link); - + CDEBUG(D_INFO, "objid "LPX64"[%d] has subobj "LPX64" at idx " - "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, + "%u\n", oa->o_id, req->rq_stripe, req->rq_oa->o_id, req->rq_idx); rc = obd_getattr_async(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, NULL, rqset); if (rc) { CERROR("error: getattr objid "LPX64" subobj " LPX64" on OST idx %d: rc = %d\n", - lovset->set_oa->o_id, req->rq_oa->o_id, + lovset->set_oa->o_id, req->rq_oa->o_id, req->rq_idx, rc); GOTO(out, rc); } lov_update_common_set(lovset, req, rc); } - + LASSERT(rc == 0); LASSERT (rqset->set_interpret == NULL); rqset->set_interpret = lov_getattr_interpret; @@ -1010,12 +1047,12 @@ static int lov_setattr_async(struct obd_export *exp, struct obdo *src_oa, obd_id objid = src_oa->o_id; int i; ENTRY; - + ASSERT_LSM_MAGIC(lsm); LASSERT(oti); if (src_oa->o_valid & OBD_MD_FLCOOKIE) LASSERT(oti->oti_logcookies); - + if (!exp || !exp->exp_obd) RETURN(-ENODEV); @@ -1077,8 +1114,8 @@ static int lov_punch(struct obd_export *exp, struct obdo *oa, list_for_each (pos, &set->set_list) { req = list_entry(pos, struct lov_request, rq_link); - rc = obd_punch(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, - NULL, req->rq_extent.start, + rc = obd_punch(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, + NULL, req->rq_extent.start, req->rq_extent.end, NULL); err = lov_update_punch_set(set, req, rc); if (err) { @@ -1118,7 +1155,7 @@ static int lov_sync(struct obd_export *exp, struct obdo *oa, list_for_each (pos, &set->set_list) { req = list_entry(pos, struct lov_request, rq_link); - rc = obd_sync(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, + rc = obd_sync(lov->tgts[req->rq_idx].ltd_exp, req->rq_oa, NULL, req->rq_extent.start, req->rq_extent.end); err = lov_update_common_set(set, req, rc); if (err) { @@ -1191,10 +1228,10 @@ static int lov_brw(int cmd, struct obd_export *exp, struct obdo *src_oa, struct obd_export *sub_exp; struct brw_page *sub_pga; req = list_entry(pos, struct lov_request, rq_link); - + sub_exp = lov->tgts[req->rq_idx].ltd_exp; sub_pga = set->set_pga + req->rq_pgaidx; - rc = obd_brw(cmd, sub_exp, req->rq_oa, req->rq_md, + rc = obd_brw(cmd, sub_exp, req->rq_oa, req->rq_md, req->rq_oabufs, sub_pga, oti); if (rc) break; @@ -1212,14 +1249,14 @@ static int lov_brw_interpret(struct ptlrpc_request_set *reqset, void *data, { struct lov_request_set *lovset = (struct lov_request_set *)data; ENTRY; - + if (rc) { lovset->set_completes = 0; lov_fini_brw_set(lovset); } else { rc = lov_fini_brw_set(lovset); } - + RETURN(rc); } @@ -1250,7 +1287,7 @@ static int lov_brw_async(int cmd, struct obd_export *exp, struct obdo *oa, struct obd_export *sub_exp; struct brw_page *sub_pga; req = list_entry(pos, struct lov_request, rq_link); - + sub_exp = lov->tgts[req->rq_idx].ltd_exp; sub_pga = lovset->set_pga + req->rq_pgaidx; rc = obd_brw_async(cmd, sub_exp, req->rq_oa, req->rq_md, @@ -1263,7 +1300,7 @@ static int lov_brw_async(int cmd, struct obd_export *exp, struct obdo *oa, LASSERT(set->set_interpret == NULL); set->set_interpret = (set_interpreter_func)lov_brw_interpret; set->set_arg = (void *)lovset; - + RETURN(rc); out: lov_fini_brw_set(lovset); @@ -1302,7 +1339,7 @@ static void lov_ap_completion(void *data, int cmd, struct obdo *oa, int rc) lap->lap_caller_ops->ap_completion(lap->lap_caller_data, cmd, oa, rc); } -static void lov_ap_get_ucred(void *data, struct obd_ucred *ouc) +static void lov_ap_get_ucred(void *data, struct lvfs_ucred *ouc) { struct lov_async_page *lap = LAP_FROM_COOKIE(data); @@ -1523,8 +1560,7 @@ static int lov_enqueue(struct obd_export *exp, struct lov_stripe_md *lsm, LASSERT(lov_lockhp); *flags = save_flags; - sub_policy.l_extent.start = req->rq_extent.start; - sub_policy.l_extent.end = req->rq_extent.end; + sub_policy.l_extent = req->rq_extent; rc = obd_enqueue(lov->tgts[req->rq_idx].ltd_exp, req->rq_md, type, &sub_policy, mode, flags, bl_cb, @@ -1567,9 +1603,8 @@ static int lov_match(struct obd_export *exp, struct lov_stripe_md *lsm, lov_lockhp = set->set_lockh->llh_handles + req->rq_stripe; LASSERT(lov_lockhp); - sub_policy.l_extent.start = req->rq_extent.start; - sub_policy.l_extent.end = req->rq_extent.end; lov_flags = *flags; + sub_policy.l_extent = req->rq_extent; rc = obd_match(lov->tgts[req->rq_idx].ltd_exp, req->rq_md, type, &sub_policy, mode, &lov_flags, data, @@ -1648,7 +1683,7 @@ static int lov_cancel(struct obd_export *exp, struct lov_stripe_md *lsm, req->rq_md->lsm_object_id, req->rq_idx, rc); err = rc; } - + } lov_fini_cancel_set(set); RETURN(err); @@ -1665,8 +1700,12 @@ static int lov_cancel_unused(struct obd_export *exp, lov = &exp->exp_obd->u.lov; if (lsm == NULL) { for (i = 0; i < lov->desc.ld_tgt_count; i++) { - int err = obd_cancel_unused(lov->tgts[i].ltd_exp, NULL, - flags, opaque); + int err; + if (!lov->tgts[i].ltd_exp) + continue; + + err = obd_cancel_unused(lov->tgts[i].ltd_exp, NULL, + flags, opaque); if (!rc) rc = err; } @@ -1722,7 +1761,7 @@ static int lov_join_lru(struct obd_export *exp, submd.lsm_object_id = loi->loi_id; submd.lsm_stripe_count = 0; - rc = obd_join_lru(lov->tgts[loi->loi_ost_idx].ltd_exp, + rc = obd_join_lru(lov->tgts[loi->loi_ost_idx].ltd_exp, &submd, join); if (rc < 0) { CERROR("join lru failed. objid: "LPX64" subobj: "LPX64 @@ -1796,9 +1835,7 @@ static int lov_statfs(struct obd_device *obd, struct obd_statfs *osfs, } if (set) { - __u32 expected_stripes = lov->desc.ld_default_stripe_count ? - lov->desc.ld_default_stripe_count : - lov->desc.ld_active_tgt_count; + __u32 expected_stripes = lov_get_stripecnt(lov, 0); if (osfs->os_files != LOV_U64_MAX) do_div(osfs->os_files, expected_stripes); @@ -1835,17 +1872,17 @@ static int lov_iocontrol(unsigned int cmd, struct obd_export *exp, int len, data = (struct obd_ioctl_data *)buf; if (sizeof(*desc) > data->ioc_inllen1) { - OBD_FREE(buf, len); + obd_ioctl_freedata(buf, len); RETURN(-EINVAL); } if (sizeof(uuidp->uuid) * count > data->ioc_inllen2) { - OBD_FREE(buf, len); + obd_ioctl_freedata(buf, len); RETURN(-EINVAL); } if (sizeof(__u32) * count > data->ioc_inllen3) { - OBD_FREE(buf, len); + obd_ioctl_freedata(buf, len); RETURN(-EINVAL); } @@ -1884,8 +1921,8 @@ static int lov_iocontrol(unsigned int cmd, struct obd_export *exp, int len, for (i = 0; i < count; i++) { int err; - /* OST was deleted */ - if (obd_uuid_empty(&lov->tgts[i].uuid)) + /* OST was disconnected */ + if (!lov->tgts[i].ltd_exp) continue; err = obd_iocontrol(cmd, lov->tgts[i].ltd_exp, @@ -1997,21 +2034,21 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen, int i, rc = 0, err; ENTRY; -#define KEY_IS(str) \ - (keylen == strlen(str) && memcmp(key, str, keylen) == 0) - if (KEY_IS("next_id")) { if (vallen != lov->desc.ld_tgt_count) RETURN(-EINVAL); + vallen = sizeof(obd_id); + } + + if (KEY_IS("next_id") || KEY_IS("checksum")) { for (i = 0; i < lov->desc.ld_tgt_count; i++) { - /* OST was deleted */ - if (obd_uuid_empty(&lov->tgts[i].uuid)) + /* OST was disconnected */ + if (!lov->tgts[i].ltd_exp) continue; - /* initialize all OSCs, even inactive ones */ - err = obd_set_info(lov->tgts[i].ltd_exp, - keylen, key, sizeof(obd_id), - ((obd_id*)val) + i); + /* hit all OSCs, even inactive ones */ + err = obd_set_info(lov->tgts[i].ltd_exp, keylen, key, + vallen, ((obd_id*)val) + i); if (!rc) rc = err; } @@ -2029,8 +2066,8 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen, if (val && !obd_uuid_equals(val, &lov->tgts[i].uuid)) continue; - /* OST was deleted */ - if (obd_uuid_empty(&lov->tgts[i].uuid)) + /* OST was disconnected */ + if (!lov->tgts[i].ltd_exp) continue; if (!val && !lov->tgts[i].active) @@ -2042,7 +2079,6 @@ static int lov_set_info(struct obd_export *exp, obd_count keylen, rc = err; } RETURN(rc); -#undef KEY_IS } int lov_test_and_clear_async_rc(struct lov_stripe_md *lsm) @@ -2150,67 +2186,10 @@ int lov_complete_many(struct obd_export *exp, struct lov_stripe_md *lsm, } #endif -static int lov_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct obd_device *obd = class_exp2obd(exp); - struct lov_obd *lov = &obd->u.lov; - int i, rc = 0; - ENTRY; - - for (i = 0; i < lov->desc.ld_tgt_count; i++) { - int err; - - if (!lov->tgts[i].active) { - CDEBUG(D_HA, "lov idx %d inactive\n", i); - continue; - } - - err = obd_quotacheck(lov->tgts[i].ltd_exp, oqctl); - if (err) { - if (lov->tgts[i].active && !rc) - rc = err; - continue; - } - } - - RETURN(rc); -} - -static int lov_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct obd_device *obd = class_exp2obd(exp); - struct lov_obd *lov = &obd->u.lov; - __u64 curspace = oqctl->qc_dqblk.dqb_curspace; - int i, rc = 0; - ENTRY; - - for (i = 0; i < lov->desc.ld_tgt_count; i++) { - int err; - - if (!lov->tgts[i].active) { - CDEBUG(D_HA, "lov idx %d inactive\n", i); - continue; - } - - err = obd_quotactl(lov->tgts[i].ltd_exp, oqctl); - if (err) { - if (lov->tgts[i].active && !rc) - rc = err; - continue; - } - - if (oqctl->qc_cmd == Q_GETQUOTA) - curspace += oqctl->qc_dqblk.dqb_curspace; - } - - if (oqctl->qc_cmd == Q_GETQUOTA) - oqctl->qc_dqblk.dqb_curspace = curspace; - RETURN(rc); -} - struct obd_ops lov_obd_ops = { .o_owner = THIS_MODULE, .o_setup = lov_setup, + .o_precleanup = lov_precleanup, .o_cleanup = lov_cleanup, .o_process_config = lov_process_config, .o_connect = lov_connect, @@ -2247,8 +2226,10 @@ struct obd_ops lov_obd_ops = { .o_llog_init = lov_llog_init, .o_llog_finish = lov_llog_finish, .o_notify = lov_notify, +#ifdef HAVE_QUOTA_SUPPORT .o_quotacheck = lov_quotacheck, .o_quotactl = lov_quotactl, +#endif }; int __init lov_init(void) diff --git a/lustre/lov/lov_pack.c b/lustre/lov/lov_pack.c index 342ad47..049ea80 100644 --- a/lustre/lov/lov_pack.c +++ b/lustre/lov/lov_pack.c @@ -124,7 +124,8 @@ int lov_packmd(struct obd_export *exp, struct lov_mds_md **lmmp, for (i = 0, loi = lsm->lsm_oinfo; i < stripe_count; i++, loi++) { /* XXX LOV STACKING call down to osc_packmd() to do packing */ - LASSERT(loi->loi_id); + LASSERTF(loi->loi_id, "lmm_oid "LPU64" stripe %u/%u idx %u\n", + lmm->lmm_object_id, i, stripe_count, loi->loi_ost_idx); lmm->lmm_objects[i].l_object_id = cpu_to_le64(loi->loi_id); lmm->lmm_objects[i].l_object_gr = cpu_to_le64(loi->loi_gr); lmm->lmm_objects[i].l_ost_gen = cpu_to_le32(loi->loi_ost_gen); @@ -138,8 +139,15 @@ int lov_get_stripecnt(struct lov_obd *lov, int stripe_count) { if (!stripe_count) stripe_count = lov->desc.ld_default_stripe_count; - if (!stripe_count || stripe_count > lov->desc.ld_active_tgt_count) + if (!stripe_count) + stripe_count = 1; + if (stripe_count > lov->desc.ld_active_tgt_count) stripe_count = lov->desc.ld_active_tgt_count; + /* for now, we limit the stripe count directly, when bug 4424 is + * fixed this needs to be somewhat dynamic based on whether ext3 + * can handle larger EA sizes. */ + if (stripe_count > LOV_MAX_STRIPE_COUNT) + stripe_count = LOV_MAX_STRIPE_COUNT; return stripe_count; } @@ -366,10 +374,10 @@ int lov_setstripe(struct obd_export *exp, struct lov_stripe_md **lsmp, /* 64kB is the largest common page size we see (ia64), and matches the * check in lfs */ - if (lum.lmm_stripe_size & (65536 - 1)) { - CDEBUG(D_IOCTL, "stripe size %u not multiple of 64kB\n", - lum.lmm_stripe_size); - RETURN(-EINVAL); + if (lum.lmm_stripe_size & (LOV_MIN_STRIPE_SIZE - 1)) { + CDEBUG(D_IOCTL, "stripe size %u not multiple of %u, fixing\n", + lum.lmm_stripe_size, LOV_MIN_STRIPE_SIZE); + lum.lmm_stripe_size = LOV_MIN_STRIPE_SIZE; } if ((lum.lmm_stripe_offset >= lov->desc.ld_active_tgt_count) && diff --git a/lustre/lov/lov_qos.c b/lustre/lov/lov_qos.c index f7b0f76..c7bd979 100644 --- a/lustre/lov/lov_qos.c +++ b/lustre/lov/lov_qos.c @@ -72,6 +72,37 @@ void qos_shrink_lsm(struct lov_request_set *set) } } +int qos_remedy_create(struct lov_request_set *set, struct lov_request *req) +{ + struct lov_stripe_md *lsm = set->set_md; + struct lov_obd *lov = &set->set_exp->exp_obd->u.lov; + unsigned ost_idx, ost_count = lov->desc.ld_tgt_count; + int stripe, i, rc = -EIO; + ENTRY; + + ost_idx = (req->rq_idx + 1) % ost_count; + for (i = 0; i < ost_count; i++, ost_idx = (ost_idx + 1) % ost_count) { + if (lov->tgts[ost_idx].active == 0) { + CDEBUG(D_HA, "lov idx %d inactive\n", ost_idx); + continue; + } + /* check if objects has been created on this ost */ + for (stripe = req->rq_stripe; stripe >= 0; stripe--) { + if (ost_idx == lsm->lsm_oinfo[stripe].loi_ost_idx) + break; + } + + if (stripe < 0) { + req->rq_idx = ost_idx; + rc = obd_create(lov->tgts[ost_idx].ltd_exp, req->rq_oa, + &req->rq_md, set->set_oti); + if (!rc) + break; + } + } + RETURN(rc); +} + #define LOV_CREATE_RESEED_INTERVAL 1000 /* FIXME use real qos data to prepare the lov create request */ int qos_prep_create(struct lov_obd *lov, struct lov_request_set *set, int newea) diff --git a/lustre/lov/lov_request.c b/lustre/lov/lov_request.c index 1ac7190..3054173 100644 --- a/lustre/lov/lov_request.c +++ b/lustre/lov/lov_request.c @@ -277,6 +277,7 @@ int lov_prep_enqueue_set(struct obd_export *exp, struct lov_stripe_md *lsm, req->rq_extent.start = start; req->rq_extent.end = end; + req->rq_extent.gid = policy->l_extent.gid; req->rq_idx = loi->loi_ost_idx; req->rq_stripe = i; @@ -384,6 +385,7 @@ int lov_prep_match_set(struct obd_export *exp, struct lov_stripe_md *lsm, req->rq_extent.start = start; req->rq_extent.end = end; + req->rq_extent.gid = policy->l_extent.gid; req->rq_idx = loi->loi_ost_idx; req->rq_stripe = i; @@ -495,9 +497,33 @@ static int create_done(struct obd_export *exp, struct lov_request_set *set, LASSERT(set->set_completes); - if (!set->set_success) - GOTO(cleanup, rc = -EIO); - if (*lsmp == NULL && set->set_count != set->set_success) { + /* try alloc objects on other osts if osc_create fails for + * exceptions: RPC failure, ENOSPC, etc */ + if (set->set_count != set->set_success) { + list_for_each_entry (req, &set->set_list, rq_link) { + if (req->rq_rc == 0) + continue; + + set->set_completes--; + req->rq_complete = 0; + + rc = qos_remedy_create(set, req); + lov_update_create_set(set, req, rc); + + if (rc) + break; + } + } + + /* no successful creates */ + if (set->set_success == 0) + GOTO(cleanup, rc); + + /* If there was an explicit stripe set, fail. Otherwise, we + * got some objects and that's not bad. */ + if (set->set_count != set->set_success) { + if (*lsmp) + GOTO(cleanup, rc); set->set_count = set->set_success; qos_shrink_lsm(set); } @@ -533,7 +559,7 @@ cleanup: if (!req->rq_complete || req->rq_rc) continue; - sub_exp = lov->tgts[req->rq_idx].ltd_exp, + sub_exp = lov->tgts[req->rq_idx].ltd_exp; err = obd_destroy(sub_exp, req->rq_oa, NULL, oti); if (err) CERROR("Failed to uncreate objid "LPX64" subobj " @@ -1203,6 +1229,7 @@ int lov_prep_punch_set(struct obd_export *exp, struct obdo *src_oa, req->rq_extent.start = rs; req->rq_extent.end = re; + req->rq_extent.gid = -1; lov_set_add_req(req, set); } @@ -1281,6 +1308,7 @@ int lov_prep_sync_set(struct obd_export *exp, struct obdo *src_oa, req->rq_extent.start = rs; req->rq_extent.end = re; + req->rq_extent.gid = -1; lov_set_add_req(req, set); } diff --git a/lustre/lov/lproc_lov.c b/lustre/lov/lproc_lov.c index ee4883d..2546c6c 100644 --- a/lustre/lov/lproc_lov.c +++ b/lustre/lov/lproc_lov.c @@ -29,11 +29,7 @@ #include <linux/obd_class.h> #include <linux/seq_file.h> -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -#else - +#ifdef LPROCFS static int lov_rd_stripesize(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -205,5 +201,5 @@ struct file_operations lov_proc_target_fops = { .release = seq_release, }; -#endif /* LPROCFS */ LPROCFS_INIT_VARS(lov, lprocfs_module_vars, lprocfs_obd_vars) +#endif /* LPROCFS */ diff --git a/lustre/lvfs/.cvsignore b/lustre/lvfs/.cvsignore index 592ce3b..d18b255 100644 --- a/lustre/lvfs/.cvsignore +++ b/lustre/lvfs/.cvsignore @@ -16,3 +16,4 @@ autoMakefile .depend sources fsfilt_ldiskfs.* +fsfilt-ldiskfs.* diff --git a/lustre/lvfs/Makefile.in b/lustre/lvfs/Makefile.in index ac5a8a2..02fb755 100644 --- a/lustre/lvfs/Makefile.in +++ b/lustre/lvfs/Makefile.in @@ -1,8 +1,9 @@ -MODULES := lvfs fsfilt_@BACKINGFS@ quotactl_test quotacheck_test +MODULES := lvfs #quotactl_test quotacheck_test +@SERVER_TRUE@MODULES += fsfilt_@BACKINGFS@ lvfs-objs := lvfs_common.o lvfs_linux.o fsfilt.o -quotactl-objs := quotactl_test.o -quotaccheck-objs := quotacheck_test.o +#quotactl-objs := quotactl_test.o +#quotaccheck-objs := quotacheck_test.o ifeq ($(PATCHLEVEL),6) fsfilt_@BACKINGFS@-objs := fsfilt-@BACKINGFS@.o diff --git a/lustre/lvfs/autoMakefile.am b/lustre/lvfs/autoMakefile.am index 814d299..ef84f9d 100644 --- a/lustre/lvfs/autoMakefile.am +++ b/lustre/lvfs/autoMakefile.am @@ -16,10 +16,16 @@ endif if MODULES -modulefs_DATA = lvfs$(KMODEXT) fsfilt_$(BACKINGFS)$(KMODEXT) +modulefs_DATA := lvfs$(KMODEXT) +if SERVER +modulefs_DATA += fsfilt_$(BACKINGFS)$(KMODEXT) sources: fsfilt_$(BACKINGFS).c touch sources +else +sources: + +endif fsfilt_extN.c: fsfilt_ext3.c sed -e "s/EXT3/EXTN/g" -e "s/ext3/extN/g" $< > $@ @@ -34,6 +40,9 @@ ldiskfs_sed_flags = \ fsfilt_ldiskfs.c: fsfilt_ext3.c sed $(strip $(ldiskfs_sed_flags)) $< > $@ +fsfilt_ldiskfs_quota.h: fsfilt_ext3_quota.h + sed $(strip $(ldiskfs_sed_flags)) $< > $@ + else @@ -43,7 +52,7 @@ endif # MODULES DIST_SOURCES = fsfilt.c fsfilt_ext3.c fsfilt_reiserfs.c lvfs_common.c \ lvfs_internal.h lvfs_linux.c lvfs_userfs.c \ - quotacheck_test.c quotactl_test.c + quotacheck_test.c quotactl_test.c # fsfilt_ext3_quota.h -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ CLEANFILES = fsfilt-*.c fsfilt_ldiskfs.c fsfilt_extN.c sources diff --git a/lustre/lvfs/fsfilt_ext3.c b/lustre/lvfs/fsfilt_ext3.c index eb890e5..c5ff7a9 100644 --- a/lustre/lvfs/fsfilt_ext3.c +++ b/lustre/lvfs/fsfilt_ext3.c @@ -112,8 +112,6 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private, case FSFILT_OP_RENAME: /* modify additional directory */ nblocks += EXT3_SINGLEDATA_TRANS_BLOCKS; - nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS + - EXT3_SINGLEDATA_TRANS_BLOCKS) * logs; /* no break */ case FSFILT_OP_SYMLINK: /* additional block + block bitmap + GDT for long symlink */ @@ -134,9 +132,6 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private, } } #endif - /* create/update logs for each stripe */ - nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS + - EXT3_SINGLEDATA_TRANS_BLOCKS) * logs; /* no break */ } case FSFILT_OP_MKDIR: @@ -148,12 +143,18 @@ static void *fsfilt_ext3_start(struct inode *inode, int op, void *desc_private, /* modify parent directory */ nblocks += EXT3_INDEX_EXTRA_TRANS_BLOCKS + EXT3_DATA_TRANS_BLOCKS; + /* create/update logs for each stripe */ + nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS + + EXT3_SINGLEDATA_TRANS_BLOCKS) * logs; break; case FSFILT_OP_SETATTR: /* Setattr on inode */ nblocks += 1; nblocks += EXT3_INDEX_EXTRA_TRANS_BLOCKS + EXT3_DATA_TRANS_BLOCKS; + /* quota chown log for each stripe */ + nblocks += (EXT3_INDEX_EXTRA_TRANS_BLOCKS + + EXT3_SINGLEDATA_TRANS_BLOCKS) * logs; break; case FSFILT_OP_CANCEL_UNLINK: /* blocks for log header bitmap update OR @@ -387,14 +388,17 @@ static int fsfilt_ext3_commit_async(struct inode *inode, void *h, static int fsfilt_ext3_commit_wait(struct inode *inode, void *h) { + journal_t *journal = EXT3_JOURNAL(inode); tid_t tid = (tid_t)(long)h; CDEBUG(D_INODE, "commit wait: %lu\n", (unsigned long) tid); - if (is_journal_aborted(EXT3_JOURNAL(inode))) + if (unlikely(is_journal_aborted(journal))) return -EIO; log_wait_commit(EXT3_JOURNAL(inode), tid); + if (unlikely(is_journal_aborted(journal))) + return -EIO; return 0; } @@ -517,17 +521,29 @@ static int fsfilt_ext3_send_bio(int rw, struct inode *inode, struct bio *bio) #else static int fsfilt_ext3_send_bio(int rw, struct inode *inode, struct kiobuf *bio) { - int rc, blocks_per_page; + int rc, blk_per_page; rc = brw_kiovec(rw, 1, &bio, inode->i_dev, KIOBUF_GET_BLOCKS(bio), 1 << inode->i_blkbits); + /* + * brw_kiovec() returns number of bytes actually written. If error + * occurred after something was written, error code is returned though + * kiobuf->errno. (See bug 6854.) + */ - blocks_per_page = PAGE_SIZE >> inode->i_blkbits; + blk_per_page = PAGE_CACHE_SIZE >> inode->i_blkbits; - if (rc != (1 << inode->i_blkbits) * bio->nr_pages * blocks_per_page) { - CERROR("short write? expected %d, wrote %d\n", - (1 << inode->i_blkbits) * bio->nr_pages * - blocks_per_page, rc); + if (rc != (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page) { + CERROR("short write? expected %d, wrote %d (%d)\n", + (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page, + rc, bio->errno); + } + if (bio->errno != 0) { + CERROR("IO error. Wrote %d of %d (%d)\n", + rc, + (1 << inode->i_blkbits) * bio->nr_pages * blk_per_page, + bio->errno); + rc = bio->errno; } return rc; @@ -811,7 +827,7 @@ static int ext3_ext_new_extent_cb(struct ext3_extents_tree *tree, /* * Putting len of the actual extent we just inserted, - * we are asking ext3_ext_walk_space() to continue + * we are asking ext3_ext_walk_space() to continue * scaning after that block */ cex->ec_len = nex.ee_len; @@ -1177,190 +1193,17 @@ static int fsfilt_ext3_get_op_len(int op, struct fsfilt_objinfo *fso, int logs) return 0; } -static const char *op_quotafile[] = { "aquota.user", "aquota.group" }; - -static int fsfilt_ext3_quotactl(struct super_block *sb, - struct obd_quotactl *oqc) -{ - int i, rc = 0, error = 0; - struct if_dqinfo *info = (struct if_dqinfo *)&oqc->qc_dqinfo; - struct if_dqblk *dqblk = (struct if_dqblk *)&oqc->qc_dqblk; - ENTRY; - - /* XXX: quotaoff */ - return -EOPNOTSUPP; - - for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) { - switch (oqc->qc_cmd) { - case Q_QUOTAON: { - rc = sb->s_qcop->quota_on(sb, i, oqc->qc_id, - (char *)op_quotafile[i]); - if (rc == -EBUSY) - error = rc; - else if (rc) - GOTO(out, rc); - break; - } - case Q_QUOTAOFF: { - sb->s_qcop->quota_off(sb, i); - break; - } - case Q_GETOINFO: - case Q_GETINFO: - rc = sb->s_qcop->get_info(sb, i, info); - GOTO(out, rc); - case Q_SETQUOTA: - rc = sb->s_qcop->set_dqblk(sb, i, oqc->qc_id, dqblk); - GOTO(out, rc); - case Q_GETOQUOTA: - case Q_GETQUOTA: - rc = sb->s_qcop->get_dqblk(sb, i, oqc->qc_id, dqblk); - GOTO(out, rc); - default: - CERROR("unsupported quotactl command: %d", oqc->qc_cmd); - LBUG(); - } - } -out: - if (!rc && error) - rc = error; - RETURN(rc); -} - -struct chkquot { - struct hlist_node cq_hash; - struct list_head cq_list; - qid_t cq_id; - short cq_type; - __u32 cq_bhardlimit; - __u32 cq_bsoftlimit; - qsize_t cq_curspace; - __u32 cq_ihardlimit; - __u32 cq_isoftlimit; - __u32 cq_curinodes; - __u64 cq_btime; - __u64 cq_itime; -}; - -static inline unsigned int const -chkquot_hash(qid_t id, int type) -{ - return (id * (MAXQUOTAS - type)) % NR_DQHASH; -} - -static inline struct chkquot * -find_chkquot(struct hlist_head *head, qid_t id, int type) -{ - struct hlist_node *node, *next; - struct chkquot *cq = NULL; - - hlist_for_each_safe(node, next, head) { - cq = hlist_entry(node, struct chkquot, cq_hash); - if (cq->cq_id == id && cq->cq_type == type) - return cq; - } - - return NULL; -} - -static struct chkquot *alloc_chkquot(qid_t id, int type) -{ - struct chkquot *cq; - - OBD_ALLOC(cq, sizeof(*cq)); - if (cq) { -#if 0 /* XXX: 2.4 doesn't support this macro */ - INIT_HLIST_NODE(&cq->cq_hash); -#endif - INIT_LIST_HEAD(&cq->cq_list); - cq->cq_id = id; - cq->cq_type = type; - } - - return cq; -} - -static struct chkquot * -cqget(struct super_block *sb, struct hlist_head *hash, struct list_head *list, - qid_t id, int type, int first_check) -{ - struct hlist_head *head = hash + chkquot_hash(id, type); - struct if_dqblk dqb; - struct chkquot *cq; - int rc; - - cq = find_chkquot(head, id, type); - if (cq) - return cq; - - cq = alloc_chkquot(id, type); - if (!cq) - return NULL; - - if (!first_check) { - rc = sb->s_qcop->get_dqblk(sb, type, id, &dqb); - if (!rc) { - cq->cq_bhardlimit = dqb.dqb_bhardlimit; - cq->cq_bsoftlimit = dqb.dqb_bsoftlimit; - cq->cq_ihardlimit = dqb.dqb_ihardlimit; - cq->cq_isoftlimit = dqb.dqb_isoftlimit; - cq->cq_btime = dqb.dqb_btime; - cq->cq_itime = dqb.dqb_itime; - } - } - - hlist_add_head(&cq->cq_hash, head); - list_add_tail(&cq->cq_list, list); - - return cq; -} - -static inline int quota_onoff(struct super_block *sb, int cmd, int type) -{ - struct obd_quotactl *oqctl; - int rc; - - OBD_ALLOC(oqctl, sizeof(*oqctl)); - - oqctl->qc_cmd = cmd; - oqctl->qc_id = QFMT_LDISKFS; - oqctl->qc_type = type; - rc = fsfilt_ext3_quotactl(sb, oqctl); - - OBD_FREE(oqctl, sizeof(*oqctl)); - return rc; -} - -static inline void read_old_dqinfo(struct super_block *sb, int type, - struct if_dqinfo *dqinfo) -{ - struct obd_quotactl *oqctl; - int rc; - ENTRY; - - OBD_ALLOC(oqctl, sizeof(*oqctl)); - - oqctl->qc_cmd = Q_GETINFO; - oqctl->qc_type = type; - rc = fsfilt_ext3_quotactl(sb, oqctl); - if (!rc) - memcpy(dqinfo + type, &oqctl->qc_dqinfo, sizeof(*dqinfo)); - - OBD_FREE(oqctl, sizeof(*oqctl)); - EXIT; -} - static inline struct ext3_group_desc * get_group_desc(struct super_block *sb, int group) { unsigned long desc_block, desc; struct ext3_group_desc *gdp; - + desc_block = group / EXT3_DESC_PER_BLOCK(sb); desc = group % EXT3_DESC_PER_BLOCK(sb); gdp = (struct ext3_group_desc *) EXT3_SB(sb)->s_group_desc[desc_block]->b_data; - + return gdp + desc; } @@ -1369,10 +1212,10 @@ read_inode_bitmap(struct super_block *sb, unsigned long group) { struct ext3_group_desc *desc; struct buffer_head *bh; - + desc = get_group_desc(sb, group); bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap)); - + return bh; } @@ -1381,344 +1224,16 @@ static inline struct inode *ext3_iget_inuse(struct super_block *sb, int index, unsigned long ino) { struct inode *inode = NULL; - + if (ext3_test_bit(index, bitmap_bh->b_data)) inode = iget(sb, ino); - - return inode; -} - -struct qchk_ctxt { - struct hlist_head hash[NR_DQHASH]; - struct list_head list; - int first_check[MAXQUOTAS]; - struct if_dqinfo dqinfo[MAXQUOTAS]; -}; - -static int add_inode_quota(struct inode *inode, struct qchk_ctxt *qctxt, - struct obd_quotactl *oqc) -{ - struct chkquot *cq; - loff_t size = 0; - qid_t qid[MAXQUOTAS]; - int cnt; - - if (!inode) - return 0; - - qid[USRQUOTA] = inode->i_uid; - qid[GRPQUOTA] = inode->i_gid; - - if (S_ISDIR(inode->i_mode) || - S_ISREG(inode->i_mode) || - S_ISLNK(inode->i_mode)) - size = inode_get_bytes(inode); - - for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (Q_TYPESET(oqc, cnt)) { - cq = cqget(inode->i_sb, qctxt->hash, &qctxt->list, qid[cnt], - cnt, qctxt->first_check[cnt]); - if (!cq) - return -ENOMEM; - - cq->cq_curspace += size; - cq->cq_curinodes ++; - } - - return 0; -} - -static int v2_write_dqheader(struct file *f, int type) -{ - int quota_magics[] = V2_INITQMAGICS; - int quota_versions[] = V2_INITQVERSIONS; - struct v2_disk_dqheader dqhead; - ssize_t size; - loff_t offset = 0; - mm_segment_t fs; - - dqhead.dqh_magic = cpu_to_le32(quota_magics[type]); - dqhead.dqh_version = cpu_to_le32(quota_versions[type]); - - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->write(f, (char *)&dqhead, sizeof(dqhead), &offset); - set_fs(fs); - if (size != sizeof(dqhead)) { - CERROR("error writing dqhead in quota file"); - return -1; - } - - return 0; -} - -/* write dqinfo struct in a new quota file */ -static int v2_write_dqinfo(struct file *f, int type, struct if_dqinfo *info) -{ - struct v2_disk_dqinfo dqinfo; - int blocks = V2_DQTREEOFF + 1; - ssize_t size; - loff_t offset = V2_DQINFOOFF; - mm_segment_t fs; - - if (info) { - dqinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); - dqinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); - dqinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK & - ~DQF_INFO_DIRTY); - } else { - dqinfo.dqi_bgrace = cpu_to_le32(MAX_DQ_TIME); - dqinfo.dqi_igrace = cpu_to_le32(MAX_IQ_TIME); - dqinfo.dqi_flags = 0; - } - - dqinfo.dqi_blocks = cpu_to_le32(blocks); - dqinfo.dqi_free_blk = 0; - dqinfo.dqi_free_entry = 0; - - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->write(f, (char *)&dqinfo, sizeof(dqinfo), &offset); - set_fs(fs); - - if (size != sizeof(dqinfo)) { - CERROR("error writing dqinfo in quota file"); - return -1; - } - - return 0; -} - -static int create_new_quota_files(struct qchk_ctxt *qctxt, - struct obd_quotactl *oqc) -{ - int i, rc = 0; - ENTRY; - - for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) { - struct if_dqinfo *info = qctxt->first_check[i]? NULL: - &qctxt->dqinfo[i]; - struct file *file; - - file = filp_open(op_quotafile[i], - O_RDWR | O_CREAT | O_TRUNC, 0644); - if (IS_ERR(file)) { - rc = PTR_ERR(file); - CERROR("can't create %s file: rc = %d\n", - op_quotafile[i], rc); - GOTO(out, rc); - } - - rc = v2_write_dqheader(file, i); - if (rc) { - filp_close(file, 0); - GOTO(out, rc = -EIO); - } - - rc = v2_write_dqinfo(file, i, info); - if (rc) { - filp_close(file, 0); - GOTO(out, rc = -EIO); - } - - filp_close(file, 0); - } - -out: - RETURN(rc); -} - - -static int commit_chkquot(struct super_block *sb, struct qchk_ctxt *qctxt, - struct chkquot *cq) -{ - struct obd_quotactl oqc = { 0, }; - struct timeval now; - - do_gettimeofday(&now); - - if (cq->cq_bsoftlimit && - toqb(cq->cq_curspace) >= cq->cq_bsoftlimit && - !cq->cq_btime) - cq->cq_btime = now.tv_sec + - qctxt->dqinfo[cq->cq_type].dqi_bgrace; - - if (cq->cq_isoftlimit && - cq->cq_curinodes >= cq->cq_isoftlimit && - !cq->cq_itime) - cq->cq_itime = now.tv_sec + - qctxt->dqinfo[cq->cq_type].dqi_igrace; - - oqc.qc_cmd = Q_SETQUOTA; - oqc.qc_type = cq->cq_type; - oqc.qc_id = cq->cq_id; - oqc.qc_dqblk.dqb_bhardlimit = cq->cq_bhardlimit; - oqc.qc_dqblk.dqb_bsoftlimit = cq->cq_bsoftlimit; - oqc.qc_dqblk.dqb_curspace = cq->cq_curspace; - oqc.qc_dqblk.dqb_ihardlimit = cq->cq_ihardlimit; - oqc.qc_dqblk.dqb_isoftlimit = cq->cq_isoftlimit; - oqc.qc_dqblk.dqb_curinodes = cq->cq_curinodes; - oqc.qc_dqblk.dqb_btime = cq->cq_btime; - oqc.qc_dqblk.dqb_itime = cq->cq_itime; - oqc.qc_dqblk.dqb_valid = QIF_ALL; - - return fsfilt_ext3_quotactl(sb, &oqc); -} - -static int prune_chkquots(struct super_block *sb, - struct qchk_ctxt *qctxt, int error) -{ - struct chkquot *cq, *tmp; - int rc; - - list_for_each_entry_safe(cq, tmp, &qctxt->list, cq_list) { - if (!error) { - rc = commit_chkquot(sb, qctxt, cq); - if (rc) - error = rc; - } - hlist_del_init(&cq->cq_hash); - list_del(&cq->cq_list); - OBD_FREE(cq, sizeof(*cq)); - } - - return error; -} - -static int fsfilt_ext3_quotacheck(struct super_block *sb, - struct obd_quotactl *oqc) -{ - struct ext3_sb_info *sbi = EXT3_SB(sb); - int i, group; - struct qchk_ctxt *qctxt; - struct buffer_head *bitmap_bh = NULL; - unsigned long ino; - struct inode *inode; - int rc; - ENTRY; - - /* turn on quota and read dqinfo if existed */ - OBD_ALLOC(qctxt, sizeof(*qctxt)); - if (!qctxt) - RETURN(-ENOMEM); - - for (i = 0; i < NR_DQHASH; i++) - INIT_HLIST_HEAD(&qctxt->hash[i]); - INIT_LIST_HEAD(&qctxt->list); - - for (i = 0; i < MAXQUOTAS; i++) if (Q_TYPESET(oqc, i)) { - rc = quota_onoff(sb, Q_QUOTAON, i); - if (!rc || rc == -EBUSY) - read_old_dqinfo(sb, i, qctxt->dqinfo); - else if (rc == -ENOENT) - qctxt->first_check[i] = 1; - else if (rc) - GOTO(out, rc); - } - - /* check quota and update in hash */ - for (group = 0; group < sbi->s_groups_count; group++) { - ino = group * sbi->s_inodes_per_group + 1; - brelse(bitmap_bh); - bitmap_bh = read_inode_bitmap(sb, group); - - for (i = 0; i < sbi->s_inodes_per_group; i++, ino++) { - if (ino < sbi->s_first_ino) - continue; - - inode = ext3_iget_inuse(sb, bitmap_bh, i, ino); - rc = add_inode_quota(inode, qctxt, oqc); - iput(inode); - if (rc) { - brelse(bitmap_bh); - GOTO(out, rc); - } - } - } - brelse(bitmap_bh); - - /* turn off quota cause we are to dump chkquot to files */ - quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type); - - rc = create_new_quota_files(qctxt, oqc); - if (rc) - GOTO(out, rc); - - /* we use vfs functions to set dqblk, so turn quota on */ - rc = quota_onoff(sb, Q_QUOTAON, oqc->qc_type); - if (rc) - GOTO(out, rc); - -out: - /* dump and free chkquot */ - rc = prune_chkquots(sb, qctxt, rc); - OBD_FREE(qctxt, sizeof(*qctxt)); - - /* turn off quota, `lfs quotacheck` will turn on when all - * nodes quotacheck finish. */ - quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type); - - if (rc) - CERROR("quotacheck failed: rc = %d\n", rc); - - oqc->qc_stat = rc; - RETURN(rc); -} - -static int fsfilt_ext3_quotainfo(struct lustre_quota_info *lqi, int type, int cmd) -{ - int rc = 0; - ENTRY; - switch (cmd) { - case QFILE_CHK: - rc = lustre_check_quota_file(lqi, type); - break; - case QFILE_RD_INFO: - rc = lustre_read_quota_info(lqi, type); - break; - case QFILE_WR_INFO: - rc = lustre_write_quota_info(lqi, type); - break; - case QFILE_INIT_INFO: - rc = lustre_init_quota_info(lqi, type); - break; - default: - CERROR("Unsupported admin quota file cmd %d\n", cmd); - LBUG(); - break; - } - RETURN(rc); + return inode; } -static int fsfilt_ext3_dquot(struct lustre_dquot *dquot, int cmd) -{ - int rc = 0; - ENTRY; - - switch (cmd) { - case QFILE_RD_DQUOT: - rc = lustre_read_dquot(dquot); - break; - case QFILE_WR_DQUOT: - if (dquot->dq_dqb.dqb_ihardlimit || - dquot->dq_dqb.dqb_isoftlimit || - dquot->dq_dqb.dqb_bhardlimit || - dquot->dq_dqb.dqb_bsoftlimit) - clear_bit(DQ_FAKE_B, &dquot->dq_flags); - else - set_bit(DQ_FAKE_B, &dquot->dq_flags); - - rc = lustre_commit_dquot(dquot); - if (rc >= 0) - rc = 0; - break; - default: - CERROR("Unsupported admin quota file cmd %d\n", cmd); - LBUG(); - break; - } - RETURN(rc); -} +#ifdef HAVE_QUOTA_SUPPORT +# include "fsfilt_ext3_quota.h" +#endif static struct fsfilt_operations fsfilt_ext3_ops = { .fs_type = "ext3", @@ -1743,10 +1258,12 @@ static struct fsfilt_operations fsfilt_ext3_ops = { .fs_setup = fsfilt_ext3_setup, .fs_send_bio = fsfilt_ext3_send_bio, .fs_get_op_len = fsfilt_ext3_get_op_len, +#ifdef HAVE_QUOTA_SUPPORT .fs_quotactl = fsfilt_ext3_quotactl, .fs_quotacheck = fsfilt_ext3_quotacheck, .fs_quotainfo = fsfilt_ext3_quotainfo, .fs_dquot = fsfilt_ext3_dquot, +#endif }; static int __init fsfilt_ext3_init(void) diff --git a/lustre/lvfs/lvfs_common.c b/lustre/lvfs/lvfs_common.c index c1a6640..d8ab4a3 100644 --- a/lustre/lvfs/lvfs_common.c +++ b/lustre/lvfs/lvfs_common.c @@ -27,7 +27,7 @@ #include <linux/lvfs.h> -struct dentry *lvfs_fid2dentry(struct obd_run_ctxt *ctxt, __u64 id, +struct dentry *lvfs_fid2dentry(struct lvfs_run_ctxt *ctxt, __u64 id, __u32 gen, __u64 gr, void *data) { return ctxt->cb_ops.l_fid2dentry(id, gen, gr, data); diff --git a/lustre/lvfs/lvfs_linux.c b/lustre/lvfs/lvfs_linux.c index 054e6ec..52f0f0d 100644 --- a/lustre/lvfs/lvfs_linux.c +++ b/lustre/lvfs/lvfs_linux.c @@ -56,8 +56,9 @@ int obd_memmax; /* Debugging check only needed during development */ #ifdef OBD_CTXT_DEBUG # define ASSERT_CTXT_MAGIC(magic) LASSERT((magic) == OBD_RUN_CTXT_MAGIC) -# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERT(!segment_eq(get_fs(), get_ds())) -# define ASSERT_KERNEL_CTXT(msg) LASSERT(segment_eq(get_fs(), get_ds())) +# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERTF(!segment_eq(get_fs(), get_ds()),\ + msg) +# define ASSERT_KERNEL_CTXT(msg) LASSERTF(segment_eq(get_fs(), get_ds()), msg) #else # define ASSERT_CTXT_MAGIC(magic) do {} while(0) # define ASSERT_NOT_KERNEL_CTXT(msg) do {} while(0) @@ -73,8 +74,8 @@ int obd_memmax; #endif /* push / pop to root of obd store */ -void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, - struct obd_ucred *uc) +void push_ctxt(struct lvfs_run_ctxt *save, struct lvfs_run_ctxt *new_ctx, + struct lvfs_ucred *uc) { //ASSERT_NOT_KERNEL_CTXT("already in kernel context!\n"); ASSERT_CTXT_MAGIC(new_ctx->magic); @@ -97,7 +98,7 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, save->pwd = dget(current->fs->pwd); save->pwdmnt = mntget(current->fs->pwdmnt); save->ngroups = current_ngroups; - save->ouc.ouc_umask = current->fs->umask; + save->luc.luc_umask = current->fs->umask; LASSERT(save->pwd); LASSERT(save->pwdmnt); @@ -105,26 +106,26 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, LASSERT(new_ctx->pwdmnt); if (uc) { - save->ouc.ouc_fsuid = current->fsuid; - save->ouc.ouc_fsgid = current->fsgid; - save->ouc.ouc_cap = current->cap_effective; - save->ouc.ouc_suppgid1 = current_groups[0]; - save->ouc.ouc_suppgid2 = current_groups[1]; - - current->fsuid = uc->ouc_fsuid; - current->fsgid = uc->ouc_fsgid; - current->cap_effective = uc->ouc_cap; + save->luc.luc_fsuid = current->fsuid; + save->luc.luc_fsgid = current->fsgid; + save->luc.luc_cap = current->cap_effective; + save->luc.luc_suppgid1 = current_groups[0]; + save->luc.luc_suppgid2 = current_groups[1]; + + current->fsuid = uc->luc_fsuid; + current->fsgid = uc->luc_fsgid; + current->cap_effective = uc->luc_cap; current_ngroups = 0; - if (uc->ouc_suppgid1 != -1) - current_groups[current_ngroups++] = uc->ouc_suppgid1; - if (uc->ouc_suppgid2 != -1) - current_groups[current_ngroups++] = uc->ouc_suppgid2; + if (uc->luc_suppgid1 != -1) + current_groups[current_ngroups++] = uc->luc_suppgid1; + if (uc->luc_suppgid2 != -1) + current_groups[current_ngroups++] = uc->luc_suppgid2; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,4) - if (uc->ouc_suppgid1 != -1 && uc->ouc_suppgid2 != -1 && - (uc->ouc_suppgid1 > uc->ouc_suppgid2)) { - current_groups[0] = uc->ouc_suppgid2; - current_groups[1] = uc->ouc_suppgid1; + if (uc->luc_suppgid1 != -1 && uc->luc_suppgid2 != -1 && + (uc->luc_suppgid1 > uc->luc_suppgid2)) { + current_groups[0] = uc->luc_suppgid2; + current_groups[1] = uc->luc_suppgid1; } #endif } @@ -145,8 +146,8 @@ void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, } EXPORT_SYMBOL(push_ctxt); -void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx, - struct obd_ucred *uc) +void pop_ctxt(struct lvfs_run_ctxt *saved, struct lvfs_run_ctxt *new_ctx, + struct lvfs_ucred *uc) { //printk("pc0"); ASSERT_CTXT_MAGIC(saved->magic); @@ -172,14 +173,14 @@ void pop_ctxt(struct obd_run_ctxt *saved, struct obd_run_ctxt *new_ctx, dput(saved->pwd); mntput(saved->pwdmnt); - current->fs->umask = saved->ouc.ouc_umask; + current->fs->umask = saved->luc.luc_umask; if (uc) { - current->fsuid = saved->ouc.ouc_fsuid; - current->fsgid = saved->ouc.ouc_fsgid; - current->cap_effective = saved->ouc.ouc_cap; + current->fsuid = saved->luc.luc_fsuid; + current->fsgid = saved->luc.luc_fsgid; + current->cap_effective = saved->luc.luc_cap; current_ngroups = saved->ngroups; - current_groups[0] = saved->ouc.ouc_suppgid1; - current_groups[1] = saved->ouc.ouc_suppgid2; + current_groups[0] = saved->luc.luc_suppgid1; + current_groups[1] = saved->luc.luc_suppgid2; } /* @@ -338,7 +339,7 @@ int lustre_fsync(struct file *file) } EXPORT_SYMBOL(lustre_fsync); -struct l_file *l_dentry_open(struct obd_run_ctxt *ctxt, struct l_dentry *de, +struct l_file *l_dentry_open(struct lvfs_run_ctxt *ctxt, struct l_dentry *de, int flags) { mntget(ctxt->pwdmnt); @@ -391,6 +392,53 @@ EXPORT_SYMBOL(l_readdir); EXPORT_SYMBOL(obd_memory); EXPORT_SYMBOL(obd_memmax); +#ifdef HAVE_OLD_DEV_SET_RDONLY +void dev_set_rdonly(lvfs_sbdev_type dev, int no_write); +void dev_clear_rdonly(int no_write); +int dev_check_rdonly(lvfs_sbdev_type dev); +#elif !defined(HAVE_CLEAR_RDONLY_ON_PUT) +void dev_set_rdonly(lvfs_sbdev_type dev); +void dev_clear_rdonly(lvfs_sbdev_type dev); +int dev_check_rdonly(lvfs_sbdev_type dev); +#endif + +void lvfs_set_rdonly(lvfs_sbdev_type dev) +{ + CDEBUG(D_IOCTL | D_HA, "set dev %lx rdonly\n", (long)dev); + lvfs_sbdev_sync(dev); +#ifdef HAVE_OLD_DEV_SET_RDONLY + dev_set_rdonly(dev, 2); +#else + dev_set_rdonly(dev); +#endif +} + +int lvfs_check_rdonly(lvfs_sbdev_type dev) +{ + return dev_check_rdonly(dev); +} + +void lvfs_clear_rdonly(lvfs_sbdev_type dev) +{ +#ifndef HAVE_CLEAR_RDONLY_ON_PUT + CDEBUG(D_IOCTL | D_HA, "unset dev %lx rdonly\n", (long)dev); + if (lvfs_check_rdonly(dev)) { + lvfs_sbdev_sync(dev); +#ifdef HAVE_OLD_DEV_SET_RDONLY + dev_clear_rdonly(2); +#else + dev_clear_rdonly(dev); +#endif + } +#else + CDEBUG(D_IOCTL | D_HA, "(will unset dev %lx rdonly on put)\n", + (long)dev); +#endif +} +EXPORT_SYMBOL(lvfs_set_rdonly); +EXPORT_SYMBOL(lvfs_check_rdonly); +EXPORT_SYMBOL(lvfs_clear_rdonly); + static int __init lvfs_linux_init(void) { RETURN(0); diff --git a/lustre/lvfs/lvfs_userfs.c b/lustre/lvfs/lvfs_userfs.c index cbdb254..a6140d5 100644 --- a/lustre/lvfs/lvfs_userfs.c +++ b/lustre/lvfs/lvfs_userfs.c @@ -31,14 +31,14 @@ #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(); } diff --git a/lustre/lvfs/quotacheck_test.c b/lustre/lvfs/quotacheck_test.c index 0791981..87c9b7b 100644 --- a/lustre/lvfs/quotacheck_test.c +++ b/lustre/lvfs/quotacheck_test.c @@ -6,18 +6,7 @@ * * This file is part of Lustre, http://www.lustre.org/ * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * No redistribution or use is permitted outside of Cluster File Systems, Inc. * * A kernel module which tests the fsfilt quotacheck API from the OBD setup function. */ @@ -204,9 +193,11 @@ static struct obd_ops quotacheck_obd_ops = { .o_cleanup = quotacheck_test_cleanup, }; +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; LPROCFS_INIT_VARS(quotacheck_test, lprocfs_module_vars, lprocfs_obd_vars) +#endif static int __init quotacheck_test_init(void) { diff --git a/lustre/lvfs/quotactl_test.c b/lustre/lvfs/quotactl_test.c index 06984c9..1e03388 100644 --- a/lustre/lvfs/quotactl_test.c +++ b/lustre/lvfs/quotactl_test.c @@ -6,18 +6,7 @@ * * This file is part of Lustre, http://www.lustre.org/ * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * No redistribution or use is permitted outside of Cluster File Systems, Inc. * * A kernel module which tests the fsfilt quotactl API from the OBD setup function. */ @@ -269,7 +258,7 @@ static int quotactl_test_4(struct obd_device *obd, struct super_block *sb) static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt) { struct super_block *sb; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc; ENTRY; @@ -282,7 +271,7 @@ static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt) RETURN(-EINVAL); } - push_ctxt(&saved, &tgt->obd_ctxt, NULL); + push_ctxt(&saved, &tgt->obd_lvfs_ctxt, NULL); rc = quotactl_test_1(tgt, sb); if (rc) @@ -301,7 +290,7 @@ static int quotactl_run_tests(struct obd_device *obd, struct obd_device *tgt) cleanup: quotactl_test_4(tgt, sb); - pop_ctxt(&saved, &tgt->obd_ctxt, NULL); + pop_ctxt(&saved, &tgt->obd_lvfs_ctxt, NULL); return rc; } @@ -348,9 +337,11 @@ static struct obd_ops quotactl_obd_ops = { .o_cleanup = quotactl_test_cleanup, }; +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; LPROCFS_INIT_VARS(quotactl_test, lprocfs_module_vars, lprocfs_obd_vars) +#endif static int __init quotactl_test_init(void) { diff --git a/lustre/mdc/autoMakefile.am b/lustre/mdc/autoMakefile.am index e46e120..e39cc9f 100644 --- a/lustre/mdc/autoMakefile.am +++ b/lustre/mdc/autoMakefile.am @@ -15,4 +15,4 @@ modulefs_DATA = mdc$(KMODEXT) endif DIST_SOURCES = $(mdc-objs:.o=.c) mdc_internal.h -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ diff --git a/lustre/mdc/lproc_mdc.c b/lustre/mdc/lproc_mdc.c index d49a771..7b83f48 100644 --- a/lustre/mdc/lproc_mdc.c +++ b/lustre/mdc/lproc_mdc.c @@ -26,10 +26,7 @@ #include <linux/obd_class.h> #include <linux/lprocfs_status.h> -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -#else +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { "ping", 0, lprocfs_wr_ping, 0 }, @@ -50,6 +47,6 @@ static struct lprocfs_vars lprocfs_module_vars[] = { { 0 } }; +LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars) #endif /* LPROCFS */ -LPROCFS_INIT_VARS(mdc, lprocfs_module_vars, lprocfs_obd_vars) diff --git a/lustre/mdc/mdc_internal.h b/lustre/mdc/mdc_internal.h index 568df2c..c08b0ab 100644 --- a/lustre/mdc/mdc_internal.h +++ b/lustre/mdc/mdc_internal.h @@ -1,3 +1,4 @@ +#include <linux/lustre_mds.h> void mdc_pack_req_body(struct ptlrpc_request *); void mdc_pack_rep_body(struct ptlrpc_request *); void mdc_readdir_pack(struct ptlrpc_request *req, __u64 offset, __u32 size, @@ -66,3 +67,27 @@ static inline void mdc_put_rpc_lock(struct mdc_rpc_lock *lck, up(&lck->rpcl_sem); } } + +/* Quota stuff */ +#ifdef HAVE_QUOTA_SUPPORT +int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl); +int mdc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk); +int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl); +#else +static inline int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} + +static inline int mdc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk) +{ + return -ENOTSUPP; +} + +static inline int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} +#endif + + diff --git a/lustre/mdc/mdc_lib.c b/lustre/mdc/mdc_lib.c index 85ce60a..3b9e023 100644 --- a/lustre/mdc/mdc_lib.c +++ b/lustre/mdc/mdc_lib.c @@ -19,7 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define DEBUG_SUBSYSTEM S_MDS +#define DEBUG_SUBSYSTEM S_MDC #ifndef __KERNEL__ # include <fcntl.h> # include <liblustre.h> @@ -249,14 +249,8 @@ void mdc_rename_pack(struct ptlrpc_request *req, int offset, rec->rn_fsuid = current->fsuid; rec->rn_fsgid = current->fsgid; rec->rn_cap = current->cap_effective; - if (in_group_p(data->ctxt.gid1)) - rec->rn_suppgid1 = data->ctxt.gid1; - else - rec->rn_suppgid1 = -1; - if (in_group_p(data->ctxt.gid2)) - rec->rn_suppgid2 = data->ctxt.gid2; - else - rec->rn_suppgid2 = -1; + rec->rn_suppgid1 = data->ctxt.gid1; + rec->rn_suppgid2 = data->ctxt.gid2; rec->rn_fid1 = data->fid1; rec->rn_fid2 = data->fid2; rec->rn_time = data->mod_time; diff --git a/lustre/mdc/mdc_locks.c b/lustre/mdc/mdc_locks.c index 6f16f23..c525a2a 100644 --- a/lustre/mdc/mdc_locks.c +++ b/lustre/mdc/mdc_locks.c @@ -164,7 +164,7 @@ int mdc_change_cbdata(struct obd_export *exp, struct ll_fid *fid, res_id.name[0] = fid->id; res_id.name[1] = fid->generation; - ldlm_change_cbdata(class_exp2obd(exp)->obd_namespace, &res_id, it, + ldlm_change_cbdata(class_exp2obd(exp)->obd_namespace, &res_id, it, data); EXIT; @@ -277,7 +277,6 @@ int mdc_enqueue(struct obd_export *exp, if (it->it_op & IT_OPEN) { it->it_create_mode |= S_IFREG; - it->it_create_mode &= ~current->fs->umask; size[2] = sizeof(struct mds_rec_create); size[3] = data->namelen + 1; @@ -329,7 +328,7 @@ int mdc_enqueue(struct obd_export *exp, reply_buffers = 4; req->rq_replen = lustre_msg_size(4, repsize); } else if (it->it_op & (IT_GETATTR | IT_LOOKUP)) { - obd_valid valid = OBD_MD_FLNOTOBD | OBD_MD_FLEASIZE; + obd_valid valid = OBD_MD_FLGETATTR | OBD_MD_FLEASIZE; size[2] = sizeof(struct mds_body); size[3] = data->namelen + 1; @@ -431,9 +430,8 @@ int mdc_enqueue(struct obd_export *exp, } if ((body->valid & OBD_MD_FLEASIZE) != 0) { - /* The eadata is opaque; just check that it is - * there. Eventually, obd_unpackmd() will check - * the contents */ + /* The eadata is opaque; just check that it is there. + * Eventually, obd_unpackmd() will check the contents */ eadata = lustre_swab_repbuf(req, 2, body->eadatasize, NULL); if (eadata == NULL) { @@ -631,7 +629,7 @@ int mdc_intent_lock(struct obd_export *exp, struct ll_uctxt *uctxt, sizeof(lockh)); } } - CDEBUG(D_DENTRY, "D_IT dentry %.*s intent: %s status %d disp %x rc %d\n", + CDEBUG(D_DENTRY,"D_IT dentry %.*s intent: %s status %d disp %x rc %d\n", len, name, ldlm_it2str(it->it_op), it->d.lustre.it_status, it->d.lustre.it_disposition, rc); diff --git a/lustre/mdc/mdc_request.c b/lustre/mdc/mdc_request.c index 1afcc33..04c0485 100644 --- a/lustre/mdc/mdc_request.c +++ b/lustre/mdc/mdc_request.c @@ -474,7 +474,14 @@ int mdc_close(struct obd_export *exp, struct obdo *oa, mod = och->och_mod; if (likely(mod != NULL)) { mod->mod_close_req = req; - LASSERT(mod->mod_open_req->rq_type != LI_POISON); + if (mod->mod_open_req->rq_type == LI_POISON) { + /* FIXME This should be an ASSERT, but until we + figure out why it can be poisoned here, give + a reasonable return. bug 6155 */ + CERROR("LBUG POISONED req %p!\n", mod->mod_open_req); + ptlrpc_free_req(req); + GOTO(out, rc = -EIO); + } DEBUG_REQ(D_HA, mod->mod_open_req, "matched open"); } else { CDEBUG(D_HA, "couldn't find open req; expecting close error\n"); @@ -611,97 +618,6 @@ int mdc_readpage(struct obd_export *exp, struct ll_fid *mdc_fid, __u64 offset, return rc; } -static int mdc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct client_obd *cli = &exp->exp_obd->u.cli; - struct ptlrpc_request *req; - struct obd_quotactl *body; - int size = sizeof(*body); - int rc; - ENTRY; - - req = ptlrpc_prep_req(class_exp2cliimp(exp), MDS_QUOTACHECK, 1, &size, - NULL); - if (!req) - GOTO(out, rc = -ENOMEM); - - body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body)); - memcpy(body, oqctl, sizeof(*body)); - - req->rq_replen = lustre_msg_size(0, NULL); - - spin_lock(&cli->cl_qchk_lock); - cli->cl_qchk_stat = CL_QUOTACHECKING; - spin_unlock(&cli->cl_qchk_lock); - rc = ptlrpc_queue_wait(req); - if (rc) { - spin_lock(&cli->cl_qchk_lock); - cli->cl_qchk_stat = rc; - spin_unlock(&cli->cl_qchk_lock); - } -out: - ptlrpc_req_finished(req); - RETURN (rc); -} - -static int mdc_poll_quotacheck(struct obd_export *exp, - struct if_quotacheck *qchk) -{ - struct client_obd *cli = &exp->exp_obd->u.cli; - int stat; - ENTRY; - - spin_lock(&cli->cl_qchk_lock); - stat = cli->cl_qchk_stat; - spin_unlock(&cli->cl_qchk_lock); - - qchk->stat = stat; - if (stat == CL_QUOTACHECKING) { - qchk->stat = -ENODATA; - stat = 0; - } else if (stat) { - if (qchk->stat > CL_QUOTACHECKING) - qchk->stat = stat = -EINTR; - - strncpy(qchk->obd_type, LUSTRE_MDS_NAME, 10); - qchk->obd_uuid = cli->cl_import->imp_target_uuid; - } - RETURN(stat); -} - -static int mdc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct ptlrpc_request *req; - struct obd_quotactl *oqc; - int size = sizeof(*oqctl); - int rc; - ENTRY; - - req = ptlrpc_prep_req(class_exp2cliimp(exp), MDS_QUOTACTL, 1, &size, - NULL); - if (!req) - GOTO(out, rc = -ENOMEM); - - memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size); - - req->rq_replen = lustre_msg_size(1, &size); - - rc = ptlrpc_queue_wait(req); - if (!rc) { - oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc), - lustre_swab_obd_quotactl); - if (oqc == NULL) { - CERROR ("Can't unpack mds_body\n"); - GOTO(out, rc = -EPROTO); - } - - memcpy(oqctl, oqc, sizeof(*oqctl)); - } -out: - ptlrpc_req_finished(req); - RETURN (rc); -} - static int mdc_iocontrol(unsigned int cmd, struct obd_export *exp, int len, void *karg, void *uarg) { @@ -715,10 +631,10 @@ static int mdc_iocontrol(unsigned int cmd, struct obd_export *exp, int len, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) MOD_INC_USE_COUNT; #else - if (!try_module_get(THIS_MODULE)) { - CERROR("Can't get module. Is it alive?"); - return -EINVAL; - } + if (!try_module_get(THIS_MODULE)) { + CERROR("Can't get module. Is it alive?"); + return -EINVAL; + } #endif switch (cmd) { case OBD_IOC_CLIENT_RECOVER: @@ -754,7 +670,7 @@ out: #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) MOD_DEC_USE_COUNT; #else - module_put(THIS_MODULE); + module_put(THIS_MODULE); #endif return rc; @@ -1040,6 +956,7 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp) { struct obd_device *obd = mdc_exp->exp_obd; struct client_obd *cli = &obd->u.cli; + struct lov_stripe_md lsm = { .lsm_magic = LOV_MAGIC }; struct lov_desc desc; __u32 valsize = sizeof(desc); int rc, size; @@ -1054,13 +971,9 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp) if (rc) RETURN(rc); - /* If default_stripe_count is zero we stripe over all OSTs */ - if (desc.ld_default_stripe_count != 0) { - struct lov_stripe_md lsm = { .lsm_magic = LOV_MAGIC, - .lsm_stripe_count = - desc.ld_default_stripe_count }; - size = obd_size_diskmd(lov_exp, &lsm); - } + lsm.lsm_stripe_count = desc.ld_default_stripe_count; + size = obd_size_diskmd(lov_exp, &lsm); + if (cli->cl_default_mds_easize < size) cli->cl_default_mds_easize = size; @@ -1071,9 +984,13 @@ int mdc_init_ea_size(struct obd_export *mdc_exp, struct obd_export *lov_exp) RETURN(0); } -static int mdc_precleanup(struct obd_device *obd) +static int mdc_precleanup(struct obd_device *obd, int stage) { int rc = 0; + ENTRY; + + if (stage < 2) + RETURN(0); rc = obd_llog_finish(obd, 0); if (rc != 0) diff --git a/lustre/mds/Makefile.in b/lustre/mds/Makefile.in index b3b3648..3e8cff6 100644 --- a/lustre/mds/Makefile.in +++ b/lustre/mds/Makefile.in @@ -3,7 +3,7 @@ mds-objs := mds_log.o mds_unlink_open.o mds_lov.o handler.o mds_reint.o mds-objs += mds_fs.o lproc_mds.o mds_open.o mds_lib.o ifeq ($(PATCHLEVEL),6) -mds-objs += quota_context.o quota_master.o +#mds-objs += quota_master.o endif @INCLUDE_RULES@ diff --git a/lustre/mds/autoMakefile.am b/lustre/mds/autoMakefile.am index 91277b5..e5bdbcf 100644 --- a/lustre/mds/autoMakefile.am +++ b/lustre/mds/autoMakefile.am @@ -7,6 +7,5 @@ if MODULES modulefs_DATA = mds$(KMODEXT) endif -MOSTLYCLEANFILES = *.o *.ko *.mod.c -DIST_SOURCES := $(mds-objs:%.o=%.c) mds_internal.h -DIST_SOURCES += quota_context.c quota_master.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ +DIST_SOURCES := $(mds-objs:%.o=%.c) mds_internal.h diff --git a/lustre/mds/handler.c b/lustre/mds/handler.c index 7dc7400..4c6e9e7 100644 --- a/lustre/mds/handler.c +++ b/lustre/mds/handler.c @@ -57,8 +57,6 @@ #include "mds_internal.h" -static struct quotacheck_info qchkinfo; - static int mds_intent_policy(struct ldlm_namespace *ns, struct ldlm_lock **lockp, void *req_cookie, ldlm_mode_t mode, int flags, void *data); @@ -141,7 +139,7 @@ static int mds_sendpage(struct ptlrpc_request *req, struct file *file, req->rq_export->exp_client_uuid.uuid, req->rq_export->exp_connection->c_remote_uuid.uuid); - ptlrpc_fail_export(req->rq_export); + class_fail_export(req->rq_export); EXIT; abort_bulk: @@ -187,11 +185,6 @@ struct dentry *mds_fid2locked_dentry(struct obd_device *obd, struct ll_fid *fid, RETURN(retval); } -#ifndef DCACHE_DISCONNECTED -#define DCACHE_DISCONNECTED DCACHE_NFSD_DISCONNECTED -#endif - - /* Look up an entry by inode number. */ /* this function ONLY returns valid dget'd dentries with an initialized inode or errors */ @@ -251,7 +244,7 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd, struct obd_uuid *cluuid, struct obd_connect_data *data) { struct obd_export *exp; - struct mds_export_data *med; /* */ + struct mds_export_data *med; struct mds_client_data *mcd; int rc, abort_recovery; ENTRY; @@ -283,8 +276,10 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd, LASSERT(exp); med = &exp->exp_mds_data; - data->ocd_connect_flags &= MDS_CONNECT_SUPPORTED; - exp->exp_connect_flags = data->ocd_connect_flags; + if (data != NULL) { + data->ocd_connect_flags &= MDS_CONNECT_SUPPORTED; + exp->exp_connect_flags = data->ocd_connect_flags; + } OBD_ALLOC(mcd, sizeof(*mcd)); if (!mcd) { @@ -296,6 +291,7 @@ static int mds_connect(struct lustre_handle *conn, struct obd_device *obd, med->med_mcd = mcd; rc = mds_client_add(obd, &obd->u.mds, med, -1); + GOTO(out, rc); out: if (rc) { @@ -324,7 +320,7 @@ static int mds_destroy_export(struct obd_export *export) { struct mds_export_data *med; struct obd_device *obd = export->exp_obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc = 0; ENTRY; @@ -334,27 +330,25 @@ static int mds_destroy_export(struct obd_export *export) if (obd_uuid_equals(&export->exp_client_uuid, &obd->obd_uuid)) GOTO(out, 0); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); /* Close any open files (which may also cause orphan unlinking). */ spin_lock(&med->med_open_lock); while (!list_empty(&med->med_open_head)) { struct list_head *tmp = med->med_open_head.next; struct mds_file_data *mfd = list_entry(tmp, struct mds_file_data, mfd_list); - BDEVNAME_DECLARE_STORAGE(btmp); - - /* bug 1579: fix force-closing for 2.5 */ struct dentry *dentry = mfd->mfd_dentry; - list_del(&mfd->mfd_list); + /* Remove mfd handle so it can't be found again. + * We are consuming the mfd_list reference here. */ + mds_mfd_unlink(mfd, 0); spin_unlock(&med->med_open_lock); /* If you change this message, be sure to update * replay_single:test_46 */ - CDEBUG(D_INODE, "force closing file handle for %.*s (%s:%lu)\n", - dentry->d_name.len, dentry->d_name.name, - ll_bdevname(dentry->d_inode->i_sb, btmp), - dentry->d_inode->i_ino); + CDEBUG(D_INODE|D_IOCTL, "%s: force closing file handle for " + "%.*s (ino %lu)\n", obd->obd_name, dentry->d_name.len, + dentry->d_name.name, dentry->d_inode->i_ino); /* child orphan sem protects orphan_dec_test and * is_orphan race, mds_mfd_close drops it */ MDS_DOWN_WRITE_ORPHAN_SEM(dentry->d_inode); @@ -362,13 +356,13 @@ static int mds_destroy_export(struct obd_export *export) !(export->exp_flags & OBD_OPT_FAILOVER)); if (rc) - CDEBUG(D_INODE, "Error closing file: %d\n", rc); + CDEBUG(D_INODE|D_IOCTL, "Error closing file: %d\n", rc); spin_lock(&med->med_open_lock); } spin_unlock(&med->med_open_lock); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); out: - mds_client_free(export, !(export->exp_flags & OBD_OPT_FAILOVER)); + mds_client_free(export); RETURN(rc); } @@ -668,10 +662,10 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req, { struct obd_device *obd = req->rq_export->exp_obd; struct ldlm_reply *rep = NULL; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct mds_body *body; struct dentry *dparent = NULL, *dchild = NULL; - struct obd_ucred uc; + struct lvfs_ucred uc; struct lustre_handle parent_lockh; int namesize; int rc = 0, cleanup_phase = 0, resent_req = 0; @@ -704,12 +698,16 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req, offset = 1; } - uc.ouc_fsuid = body->fsuid; - uc.ouc_fsgid = body->fsgid; - uc.ouc_cap = body->capability; - uc.ouc_suppgid1 = body->suppgid; - uc.ouc_suppgid2 = -1; - push_ctxt(&saved, &obd->obd_ctxt, &uc); +#if CRAY_PORTALS + uc.luc_fsuid = req->rq_uid; +#else + uc.luc_fsuid = body->fsuid; +#endif + uc.luc_fsgid = body->fsgid; + uc.luc_cap = body->capability; + uc.luc_suppgid1 = body->suppgid; + uc.luc_suppgid2 = -1; + push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc); cleanup_phase = 1; /* kernel context */ intent_set_disposition(rep, DISP_LOOKUP_EXECD); @@ -800,7 +798,7 @@ static int mds_getattr_name(int offset, struct ptlrpc_request *req, } l_dput(dchild); case 1: - pop_ctxt(&saved, &obd->obd_ctxt, &uc); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc); default: ; } return rc; @@ -810,10 +808,10 @@ static int mds_getattr(int offset, struct ptlrpc_request *req) { struct mds_obd *mds = mds_req2mds(req); struct obd_device *obd = req->rq_export->exp_obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct dentry *de; struct mds_body *body; - struct obd_ucred uc; + struct lvfs_ucred uc; int rc = 0; ENTRY; @@ -824,10 +822,14 @@ static int mds_getattr(int offset, struct ptlrpc_request *req) RETURN(-EFAULT); } - uc.ouc_fsuid = body->fsuid; - uc.ouc_fsgid = body->fsgid; - uc.ouc_cap = body->capability; - push_ctxt(&saved, &obd->obd_ctxt, &uc); +#if CRAY_PORTALS + uc.luc_fsuid = req->rq_uid; +#else + uc.luc_fsuid = body->fsuid; +#endif + uc.luc_fsgid = body->fsgid; + uc.luc_cap = body->capability; + push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc); de = mds_fid2dentry(mds, &body->fid1, NULL); if (IS_ERR(de)) { rc = req->rq_status = PTR_ERR(de); @@ -845,7 +847,7 @@ static int mds_getattr(int offset, struct ptlrpc_request *req) l_dput(de); GOTO(out_pop, rc); out_pop: - pop_ctxt(&saved, &obd->obd_ctxt, &uc); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc); return rc; } @@ -904,7 +906,7 @@ static int mds_sync(struct ptlrpc_request *req) body = lustre_swab_reqbuf(req, 0, sizeof(*body), lustre_swab_mds_body); if (body == NULL) - GOTO(out, rc = -EPROTO); + GOTO(out, rc = -EFAULT); rc = lustre_pack_reply(req, 1, &size, NULL); if (rc || OBD_FAIL_CHECK(OBD_FAIL_MDS_SYNC_PACK)) { @@ -952,9 +954,9 @@ static int mds_readpage(struct ptlrpc_request *req) struct dentry *de; struct file *file; struct mds_body *body, *repbody; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc, size = sizeof(*repbody); - struct obd_ucred uc; + struct lvfs_ucred uc; ENTRY; if (OBD_FAIL_CHECK(OBD_FAIL_MDS_READPAGE_PACK)) @@ -970,10 +972,14 @@ static int mds_readpage(struct ptlrpc_request *req) if (body == NULL) GOTO (out, rc = -EFAULT); - uc.ouc_fsuid = body->fsuid; - uc.ouc_fsgid = body->fsgid; - uc.ouc_cap = body->capability; - push_ctxt(&saved, &obd->obd_ctxt, &uc); +#if CRAY_PORTALS + uc.luc_fsuid = req->rq_uid; +#else + uc.luc_fsuid = body->fsuid; +#endif + uc.luc_fsgid = body->fsgid; + uc.luc_cap = body->capability; + push_ctxt(&saved, &obd->obd_lvfs_ctxt, &uc); de = mds_fid2dentry(&obd->u.mds, &body->fid1, &mnt); if (IS_ERR(de)) GOTO(out_pop, rc = PTR_ERR(de)); @@ -1012,206 +1018,7 @@ static int mds_readpage(struct ptlrpc_request *req) out_file: filp_close(file, 0); out_pop: - pop_ctxt(&saved, &obd->obd_ctxt, &uc); -out: - req->rq_status = rc; - RETURN(0); -} - -static int mds_quotacheck_callback(struct obd_export *exp, - struct obd_quotactl *oqctl) -{ - struct ptlrpc_request *req; - struct obd_quotactl *body; - int rc, size = sizeof(*oqctl); - - req = ptlrpc_prep_req(exp->exp_imp_reverse, OBD_QC_CALLBACK, - 1, &size, NULL); - if (!req) - RETURN(-ENOMEM); - - body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body)); - memcpy(body, oqctl, sizeof(*oqctl)); - - req->rq_replen = lustre_msg_size(0, NULL); - - rc = ptlrpc_queue_wait(req); - ptlrpc_req_finished(req); - - RETURN(rc); -} - - -static int mds_quotacheck_thread(void *data) -{ - unsigned long flags; - struct quotacheck_info *qchki = data; - struct obd_device *obd; - struct obd_export *exp; - struct obd_quotactl *oqctl; - struct obd_run_ctxt saved; - int rc; - - lock_kernel(); - ptlrpc_daemonize(); - - SIGNAL_MASK_LOCK(current, flags); - sigfillset(¤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); @@ -1617,12 +1424,12 @@ int mds_update_server_data(struct obd_device *obd, int force_sync) struct mds_obd *mds = &obd->u.mds; struct mds_server_data *msd = mds->mds_server_data; struct file *filp = mds->mds_rcvd_filp; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; loff_t off = 0; int rc; ENTRY; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); msd->msd_last_transno = cpu_to_le64(mds->mds_last_transno); CDEBUG(D_SUPER, "MDS mount_count is "LPU64", last_transno is "LPU64"\n", @@ -1630,7 +1437,7 @@ int mds_update_server_data(struct obd_device *obd, int force_sync) rc = fsfilt_write_record(obd, filp, msd, sizeof(*msd), &off,force_sync); if (rc) CERROR("error writing MDS server data: rc = %d\n", rc); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -1686,17 +1493,14 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf) CDEBUG(D_SUPER, "%s: mnt = %p\n", lustre_cfg_string(lcfg, 1), mnt); - LASSERT(!ll_check_rdonly(ll_sbdev(mnt->mnt_sb))); + LASSERT(!lvfs_check_rdonly(lvfs_sbdev(mnt->mnt_sb))); - sema_init(&mds->mds_quota_info.qi_sem, 1); sema_init(&mds->mds_orphan_recovery_sem, 1); sema_init(&mds->mds_epoch_sem, 1); spin_lock_init(&mds->mds_transno_lock); mds->mds_max_mdsize = sizeof(struct lov_mds_md); mds->mds_max_cookiesize = sizeof(struct llog_cookie); - atomic_set(&mds->mds_quotachecking, 1); - sprintf(ns_name, "mds-%s", obd->obd_uuid.uuid); obd->obd_namespace = ldlm_namespace_new(ns_name, LDLM_NAMESPACE_SERVER); if (obd->obd_namespace == NULL) { @@ -1707,7 +1511,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf) rc = mds_fs_setup(obd, mnt); if (rc) { - CERROR("MDS filesystem method init failed: rc = %d\n", rc); + CERROR("%s: MDS filesystem method init failed: rc = %d\n", + obd->obd_name, rc); GOTO(err_ns, rc); } @@ -1734,6 +1539,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf) "mds_ldlm_client", &obd->obd_ldlm_client); obd->obd_replayable = 1; + mds_quota_setup(mds); + rc = mds_postsetup(obd); if (rc) GOTO(err_fs, rc); @@ -1763,13 +1570,8 @@ static int mds_setup(struct obd_device *obd, obd_count len, void *buf) obd->obd_replayable ? "enabled" : "disabled"); } - sema_init(&mds->mds_quota_info.qi_sem, 1); - rc = qctxt_init(&mds->mds_quota_ctxt, mds->mds_sb, dqacq_handler); - if (rc) { - CERROR("initialize quota context failed! (rc:%d)\n", rc); - qctxt_cleanup(&mds->mds_quota_ctxt, 0); - GOTO(err_fs, rc); - } + ldlm_timeout = 6; + ping_evictor_start(); RETURN(0); @@ -1801,16 +1603,16 @@ static int mds_postsetup(struct obd_device *obd) RETURN(rc); if (mds->mds_profile) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct lustre_profile *lprof; struct config_llog_instance cfg; cfg.cfg_instance = NULL; cfg.cfg_uuid = mds->mds_lov_uuid; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = class_config_parse_llog(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), mds->mds_profile, &cfg); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); switch (rc) { case 0: break; @@ -1851,6 +1653,10 @@ int mds_postrecov(struct obd_device *obd) { struct mds_obd *mds = &obd->u.mds; int rc, item = 0; + ENTRY; + + if (obd->obd_fail) + RETURN(0); LASSERT(!obd->obd_recovering); LASSERT(llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT) != NULL); @@ -1892,7 +1698,7 @@ int mds_postrecov(struct obd_device *obd) } out: - RETURN(rc < 0 ? rc: item); + RETURN(rc < 0 ? rc : item); err_llog: /* cleanup all llogging subsystems */ @@ -1910,7 +1716,7 @@ int mds_lov_clean(struct obd_device *obd) if (mds->mds_profile) { char * cln_prof; struct config_llog_instance cfg; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int len = strlen(mds->mds_profile) + sizeof("-clean") + 1; OBD_ALLOC(cln_prof, len); @@ -1919,10 +1725,10 @@ int mds_lov_clean(struct obd_device *obd) cfg.cfg_instance = NULL; cfg.cfg_uuid = mds->mds_lov_uuid; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); class_config_parse_llog(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), cln_prof, &cfg); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); OBD_FREE(cln_prof, len); OBD_FREE(mds->mds_profile, strlen(mds->mds_profile) + 1); @@ -1931,30 +1737,38 @@ int mds_lov_clean(struct obd_device *obd) RETURN(0); } -static int mds_precleanup(struct obd_device *obd) +static int mds_precleanup(struct obd_device *obd, int stage) { int rc = 0; ENTRY; - mds_lov_set_cleanup_flags(obd); - target_cleanup_recovery(obd); - mds_lov_disconnect(obd); - mds_lov_clean(obd); - llog_cleanup(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT)); + switch (stage) { + case 1: + mds_lov_set_cleanup_flags(obd); + target_cleanup_recovery(obd); + break; + case 2: + mds_lov_disconnect(obd); + mds_lov_clean(obd); + llog_cleanup(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT)); + rc = obd_llog_finish(obd, 0); + } RETURN(rc); } static int mds_cleanup(struct obd_device *obd) { struct mds_obd *mds = &obd->u.mds; - ll_sbdev_type save_dev; + lvfs_sbdev_type save_dev; int must_relock = 0; ENTRY; + ping_evictor_stop(); + if (mds->mds_sb == NULL) RETURN(0); - save_dev = ll_sbdev(mds->mds_sb); - + save_dev = lvfs_sbdev(mds->mds_sb); + if (mds->mds_osc_exp) /* lov export was disconnected by mds_lov_clean; we just need to drop our ref */ @@ -1962,7 +1776,7 @@ static int mds_cleanup(struct obd_device *obd) lprocfs_obd_cleanup(obd); - qctxt_cleanup(&mds->mds_quota_ctxt, 0); + mds_quota_cleanup(mds); mds_update_server_data(obd, 1); if (mds->mds_lov_objids != NULL) { @@ -1984,8 +1798,6 @@ static int mds_cleanup(struct obd_device *obd) must_relock++; } - obd_llog_finish(obd, 0); - mntput(mds->mds_vfsmnt); mds->mds_sb = NULL; @@ -1998,8 +1810,8 @@ static int mds_cleanup(struct obd_device *obd) } spin_unlock_bh(&obd->obd_processing_task_lock); - ll_clear_rdonly(save_dev); - + lvfs_clear_rdonly(save_dev); + if (must_relock) lock_kernel(); @@ -2047,7 +1859,8 @@ static void fixup_handle_for_resent_req(struct ptlrpc_request *req, /* If the xid matches, then we know this is a resent request, * and allow it. (It's probably an OPEN, for which we don't * send a lock */ - if (req->rq_xid == exp->exp_mds_data.med_mcd->mcd_last_xid) + if (req->rq_xid == + le64_to_cpu(exp->exp_mds_data.med_mcd->mcd_last_xid)) return; /* This remote handle isn't enqueued, so we never received or diff --git a/lustre/mds/lproc_mds.c b/lustre/mds/lproc_mds.c index 74795b4..fe5d6f5 100644 --- a/lustre/mds/lproc_mds.c +++ b/lustre/mds/lproc_mds.c @@ -29,14 +29,9 @@ #include <linux/obd_class.h> #include <linux/lprocfs_status.h> -#ifndef LPROCFS -struct lprocfs_vars lprocfs_mds_obd_vars[] = { {0} }; -struct lprocfs_vars lprocfs_mds_module_vars[] = { {0} }; -struct lprocfs_vars lprocfs_mdt_obd_vars[] = { {0} }; -struct lprocfs_vars lprocfs_mdt_module_vars[] = { {0} }; - -#else +#include "mds_internal.h" +#ifdef LPROCFS static int lprocfs_mds_rd_mntdev(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -49,121 +44,6 @@ static int lprocfs_mds_rd_mntdev(char *page, char **start, off_t off, int count, return snprintf(page, count, "%s\n",obd->u.mds.mds_vfsmnt->mnt_devname); } -static int lprocfs_mds_rd_bunit(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.mds.mds_quota_ctxt.lqc_bunit_sz); -} - -static int lprocfs_mds_rd_iunit(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.mds.mds_quota_ctxt.lqc_iunit_sz); -} - -static int lprocfs_mds_wr_bunit(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val % QUOTABLOCK_SIZE || - val <= obd->u.mds.mds_quota_ctxt.lqc_btune_sz) - return -EINVAL; - - obd->u.mds.mds_quota_ctxt.lqc_bunit_sz = val; - return count; -} - -static int lprocfs_mds_wr_iunit(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= obd->u.mds.mds_quota_ctxt.lqc_itune_sz) - return -EINVAL; - - obd->u.mds.mds_quota_ctxt.lqc_iunit_sz = val; - return count; -} - -static int lprocfs_mds_rd_btune(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.mds.mds_quota_ctxt.lqc_btune_sz); -} - -static int lprocfs_mds_rd_itune(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.mds.mds_quota_ctxt.lqc_itune_sz); -} - -static int lprocfs_mds_wr_btune(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || - val >= obd->u.mds.mds_quota_ctxt.lqc_bunit_sz) - return -EINVAL; - - obd->u.mds.mds_quota_ctxt.lqc_btune_sz = val; - return count; -} - -static int lprocfs_mds_wr_itune(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= MIN_QLIMIT || - val >= obd->u.mds.mds_quota_ctxt.lqc_iunit_sz) - return -EINVAL; - - obd->u.mds.mds_quota_ctxt.lqc_itune_sz = val; - return count; -} - struct lprocfs_vars lprocfs_mds_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { "blocksize", lprocfs_rd_blksize, 0, 0 }, @@ -177,10 +57,12 @@ struct lprocfs_vars lprocfs_mds_obd_vars[] = { { "recovery_status", lprocfs_obd_rd_recovery_status, 0, 0 }, { "evict_client", 0, lprocfs_wr_evict_client, 0 }, { "num_exports", lprocfs_rd_num_exports, 0, 0 }, +#ifdef HAVE_QUOTA_SUPPORT { "quota_bunit_sz", lprocfs_mds_rd_bunit, lprocfs_mds_wr_bunit, 0 }, { "quota_btune_sz", lprocfs_mds_rd_btune, lprocfs_mds_wr_btune, 0 }, { "quota_iunit_sz", lprocfs_mds_rd_iunit, lprocfs_mds_wr_iunit, 0 }, { "quota_itune_sz", lprocfs_mds_rd_itune, lprocfs_mds_wr_itune, 0 }, +#endif { 0 } }; @@ -199,6 +81,6 @@ struct lprocfs_vars lprocfs_mdt_module_vars[] = { { 0 } }; -#endif LPROCFS_INIT_VARS(mds, lprocfs_mds_module_vars, lprocfs_mds_obd_vars); LPROCFS_INIT_VARS(mdt, lprocfs_mdt_module_vars, lprocfs_mdt_obd_vars); +#endif diff --git a/lustre/mds/mds_fs.c b/lustre/mds/mds_fs.c index a71a5c2..45d0859 100644 --- a/lustre/mds/mds_fs.c +++ b/lustre/mds/mds_fs.c @@ -32,7 +32,7 @@ #include <linux/kmod.h> #include <linux/version.h> #include <linux/sched.h> -#include <linux/quotaops.h> +#include <linux/lustre_quota.h> #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) #include <linux/mount.h> #endif @@ -47,7 +47,6 @@ /* This limit is arbitrary, but for now we fit it in 1 page (32k clients) */ #define MDS_MAX_CLIENTS (PAGE_SIZE * 8) -#define MDS_MAX_CLIENT_WORDS (MDS_MAX_CLIENTS / sizeof(unsigned long)) #define LAST_RCVD "last_rcvd" #define LOV_OBJID "lov_objid" @@ -56,6 +55,9 @@ * in the last_rcvd file if cl_off is -1 (i.e. a new client). * Otherwise, we have just read the data from the last_rcvd file and * we know its offset. + * + * It should not be possible to fail adding an existing client - otherwise + * mds_init_server_data() callsite needs to be fixed. */ int mds_client_add(struct obd_device *obd, struct mds_obd *mds, struct mds_export_data *med, int cl_idx) @@ -65,6 +67,7 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds, ENTRY; LASSERT(bitmap != NULL); + LASSERTF(cl_idx > -2, "%d\n", cl_idx); /* XXX if mcd_uuid were a real obd_uuid, I could use obd_uuid_equals */ if (!strcmp(med->med_mcd->mcd_uuid, obd->obd_uuid.uuid)) @@ -76,9 +79,10 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds, if (new_client) { cl_idx = find_first_zero_bit(bitmap, MDS_MAX_CLIENTS); repeat: - if (cl_idx >= MDS_MAX_CLIENTS) { + if (cl_idx >= MDS_MAX_CLIENTS || + OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_CLIENT_ADD)) { CERROR("no room for clients - fix MDS_MAX_CLIENTS\n"); - return -ENOMEM; + return -EOVERFLOW; } if (test_and_set_bit(cl_idx, bitmap)) { cl_idx = find_next_zero_bit(bitmap, MDS_MAX_CLIENTS, @@ -96,79 +100,92 @@ int mds_client_add(struct obd_device *obd, struct mds_obd *mds, CDEBUG(D_INFO, "client at idx %d with UUID '%s' added\n", cl_idx, med->med_mcd->mcd_uuid); - med->med_idx = cl_idx; - med->med_off = le32_to_cpu(mds->mds_server_data->msd_client_start) + + med->med_lr_idx = cl_idx; + med->med_lr_off = le32_to_cpu(mds->mds_server_data->msd_client_start) + (cl_idx * le16_to_cpu(mds->mds_server_data->msd_client_size)); + LASSERTF(med->med_lr_off > 0, "med_lr_off = %llu\n", med->med_lr_off); if (new_client) { - struct obd_run_ctxt saved; - loff_t off = med->med_off; + struct lvfs_run_ctxt saved; + loff_t off = med->med_lr_off; struct file *file = mds->mds_rcvd_filp; int rc; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_write_record(obd, file, med->med_mcd, sizeof(*med->med_mcd), &off, 1); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc) return rc; CDEBUG(D_INFO, "wrote client mcd at idx %u off %llu (len %u)\n", - med->med_idx, med->med_off, + med->med_lr_idx, med->med_lr_off, (unsigned int)sizeof(*med->med_mcd)); } return 0; } -int mds_client_free(struct obd_export *exp, int clear_client) +int mds_client_free(struct obd_export *exp) { struct mds_export_data *med = &exp->exp_mds_data; struct mds_obd *mds = &exp->exp_obd->u.mds; struct obd_device *obd = exp->exp_obd; struct mds_client_data zero_mcd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc; - unsigned long *bitmap = mds->mds_client_bitmap; + loff_t off; + ENTRY; if (!med->med_mcd) RETURN(0); /* XXX if mcd_uuid were a real obd_uuid, I could use obd_uuid_equals */ if (!strcmp(med->med_mcd->mcd_uuid, obd->obd_uuid.uuid)) - GOTO(free_and_out, 0); + GOTO(free, 0); + + CDEBUG(D_INFO, "freeing client at idx %u, offset %lld with UUID '%s'\n", + med->med_lr_idx, med->med_lr_off, med->med_mcd->mcd_uuid); - CDEBUG(D_INFO, "freeing client at idx %u (%lld)with UUID '%s'\n", - med->med_idx, med->med_off, med->med_mcd->mcd_uuid); + LASSERT(mds->mds_client_bitmap != NULL); - LASSERT(bitmap); + off = med->med_lr_off; + + /* Don't clear med_lr_idx here as it is likely also unset. At worst + * we leak a client slot that will be cleaned on the next recovery. */ + if (off <= 0) { + CERROR("%s: client idx %d has offset %lld\n", + obd->obd_name, med->med_lr_idx, off); + GOTO(free, rc = -EINVAL); + } /* Clear the bit _after_ zeroing out the client so we don't race with mds_client_add and zero out new clients.*/ - if (!test_bit(med->med_idx, bitmap)) { + if (!test_bit(med->med_lr_idx, mds->mds_client_bitmap)) { CERROR("MDS client %u: bit already clear in bitmap!!\n", - med->med_idx); + med->med_lr_idx); LBUG(); } - if (clear_client) { + if (!(exp->exp_flags & OBD_OPT_FAILOVER)) { memset(&zero_mcd, 0, sizeof zero_mcd); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = fsfilt_write_record(obd, mds->mds_rcvd_filp, &zero_mcd, - sizeof(zero_mcd), &med->med_off, 1); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + sizeof(zero_mcd), &off, 1); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); CDEBUG(rc == 0 ? D_INFO : D_ERROR, "zeroing out client %s idx %u in %s rc %d\n", - med->med_mcd->mcd_uuid, med->med_idx, LAST_RCVD, rc); + med->med_mcd->mcd_uuid, med->med_lr_idx, LAST_RCVD, rc); } - if (!test_and_clear_bit(med->med_idx, bitmap)) { + if (!test_and_clear_bit(med->med_lr_idx, mds->mds_client_bitmap)) { CERROR("MDS client %u: bit already clear in bitmap!!\n", - med->med_idx); + med->med_lr_idx); LBUG(); } - free_and_out: + EXIT; +free: OBD_FREE(med->med_mcd, sizeof(*med->med_mcd)); med->med_mcd = NULL; @@ -177,8 +194,7 @@ int mds_client_free(struct obd_export *exp, int clear_client) static int mds_server_free_data(struct mds_obd *mds) { - OBD_FREE(mds->mds_client_bitmap, - MDS_MAX_CLIENT_WORDS * sizeof(unsigned long)); + OBD_FREE(mds->mds_client_bitmap, MDS_MAX_CLIENTS / 8); OBD_FREE(mds->mds_server_data, sizeof(*mds->mds_server_data)); mds->mds_server_data = NULL; @@ -206,8 +222,7 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file) if (!msd) RETURN(-ENOMEM); - OBD_ALLOC_WAIT(mds->mds_client_bitmap, - MDS_MAX_CLIENT_WORDS * sizeof(unsigned long)); + OBD_ALLOC_WAIT(mds->mds_client_bitmap, MDS_MAX_CLIENTS / 8); if (!mds->mds_client_bitmap) { OBD_FREE(msd, sizeof(*msd)); RETURN(-ENOMEM); @@ -323,7 +338,9 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file) sizeof exp->exp_client_uuid.uuid); med = &exp->exp_mds_data; med->med_mcd = mcd; - mds_client_add(obd, mds, med, cl_idx); + rc = mds_client_add(obd, mds, med, cl_idx); + LASSERTF(rc == 0, "rc = %d\n", rc); /* can't fail existing */ + /* create helper if export init gets more complex */ INIT_LIST_HEAD(&med->med_open_head); spin_lock_init(&med->med_open_lock); @@ -338,7 +355,7 @@ static int mds_init_server_data(struct obd_device *obd, struct file *file) cl_idx, last_transno); if (last_transno > mds->mds_last_transno) - mds->mds_last_transno = last_transno; + mds->mds_last_transno = last_transno; } if (mcd) @@ -375,7 +392,7 @@ err_msd: int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt) { struct mds_obd *mds = &obd->u.mds; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct dentry *dentry; struct file *file; int rc; @@ -390,14 +407,14 @@ int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt) fsfilt_setup(obd, mds->mds_sb); - OBD_SET_CTXT_MAGIC(&obd->obd_ctxt); - obd->obd_ctxt.pwdmnt = mnt; - obd->obd_ctxt.pwd = mnt->mnt_root; - obd->obd_ctxt.fs = get_ds(); - obd->obd_ctxt.cb_ops = mds_lvfs_ops; + OBD_SET_CTXT_MAGIC(&obd->obd_lvfs_ctxt); + obd->obd_lvfs_ctxt.pwdmnt = mnt; + obd->obd_lvfs_ctxt.pwd = mnt->mnt_root; + obd->obd_lvfs_ctxt.fs = get_ds(); + obd->obd_lvfs_ctxt.cb_ops = mds_lvfs_ops; /* setup the directory tree */ - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); dentry = simple_mkdir(current->fs->pwd, "ROOT", 0755, 0); if (IS_ERR(dentry)) { rc = PTR_ERR(dentry); @@ -484,7 +501,7 @@ int mds_fs_setup(struct obd_device *obd, struct vfsmount *mnt) GOTO(err_lov_objid, rc = -ENOENT); } err_pop: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); return rc; @@ -511,8 +528,8 @@ err_fid: int mds_fs_cleanup(struct obd_device *obd) { struct mds_obd *mds = &obd->u.mds; - struct obd_run_ctxt saved; - int i, rc = 0; + struct lvfs_run_ctxt saved; + int rc = 0; if (obd->obd_fail) CERROR("%s: shutting down for failover; client state will" @@ -521,7 +538,7 @@ int mds_fs_cleanup(struct obd_device *obd) class_disconnect_exports(obd); /* cleans up client info too */ mds_server_free_data(mds); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (mds->mds_rcvd_filp) { rc = filp_close(mds->mds_rcvd_filp, 0); mds->mds_rcvd_filp = NULL; @@ -546,21 +563,13 @@ int mds_fs_cleanup(struct obd_device *obd) l_dput(mds->mds_pending_dir); mds->mds_pending_dir = NULL; } - - /* close admin quota files */ - down(&mds->mds_quota_info.qi_sem); - for (i = 0; i < MAXQUOTAS; i++) { - if (mds->mds_quota_info.qi_files[i]) { - filp_close(mds->mds_quota_info.qi_files[i], 0); - mds->mds_quota_info.qi_files[i] = NULL; - } - } - up(&mds->mds_quota_info.qi_sem); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + mds_fs_quota_cleanup(mds); + + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); shrink_dcache_parent(mds->mds_fid_de); dput(mds->mds_fid_de); - DQUOT_OFF(mds->mds_sb); + LL_DQUOT_OFF(mds->mds_sb); return rc; } @@ -576,18 +585,18 @@ int mds_obd_create(struct obd_export *exp, struct obdo *oa, unsigned int tmpname = ll_insecure_random_int(); struct file *filp; struct dentry *new_child; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; char fidname[LL_FID_NAMELEN]; void *handle; - struct obd_ucred ucred; + struct lvfs_ucred ucred; int rc = 0, err, namelen; ENTRY; /* the owner of object file should always be root */ memset(&ucred, 0, sizeof(ucred)); - ucred.ouc_cap = current->cap_effective | CAP_SYS_RESOURCE; + ucred.luc_cap = current->cap_effective | CAP_SYS_RESOURCE; - push_ctxt(&saved, &exp->exp_obd->obd_ctxt, &ucred); + push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, &ucred); sprintf(fidname, "OBJECTS/%u.%u", tmpname, current->pid); filp = filp_open(fidname, O_CREAT | O_EXCL, 0666); @@ -651,7 +660,7 @@ out_close: rc = err; } out_pop: - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, &ucred); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, &ucred); RETURN(rc); } @@ -661,8 +670,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, struct mds_obd *mds = &exp->exp_obd->u.mds; struct inode *parent_inode = mds->mds_objects_dir->d_inode; struct obd_device *obd = exp->exp_obd; - struct obd_run_ctxt saved; - struct obd_ucred ucred; + struct lvfs_run_ctxt saved; + struct lvfs_ucred ucred; char fidname[LL_FID_NAMELEN]; struct dentry *de; void *handle; @@ -670,8 +679,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, ENTRY; memset(&ucred, 0, sizeof(ucred)); - ucred.ouc_cap = current->cap_effective | CAP_SYS_RESOURCE; - push_ctxt(&saved, &obd->obd_ctxt, &ucred); + ucred.luc_cap = current->cap_effective | CAP_SYS_RESOURCE; + push_ctxt(&saved, &obd->obd_lvfs_ctxt, &ucred); namelen = ll_fid2str(fidname, oa->o_id, oa->o_generation); @@ -685,8 +694,8 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, GOTO(out_dput, rc); } if (de->d_inode == NULL) { - CERROR("destroying non-existent object "LPU64" %s\n", - oa->o_id, fidname); + CERROR("destroying non-existent object "LPU64" %s: rc %d\n", + oa->o_id, fidname, rc); GOTO(out_dput, rc = -ENOENT); } @@ -694,10 +703,10 @@ int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, that is unlinked, not spanned across multiple OSTs */ handle = fsfilt_start_log(obd, mds->mds_objects_dir->d_inode, FSFILT_OP_UNLINK, oti, 1); - if (IS_ERR(handle)) { - GOTO(out_dput, rc = PTR_ERR(handle)); - } + if (IS_ERR(handle)) + GOTO(out_dput, rc = PTR_ERR(handle)); + rc = vfs_unlink(mds->mds_objects_dir->d_inode, de); if (rc) CERROR("error destroying object "LPU64":%u: rc %d\n", @@ -711,6 +720,6 @@ out_dput: l_dput(de); up(&parent_inode->i_sem); - pop_ctxt(&saved, &obd->obd_ctxt, &ucred); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &ucred); RETURN(rc); } diff --git a/lustre/mds/mds_internal.h b/lustre/mds/mds_internal.h index 792d6d0..ceca1cb 100644 --- a/lustre/mds/mds_internal.h +++ b/lustre/mds/mds_internal.h @@ -147,7 +147,6 @@ int mds_lov_disconnect(struct obd_device *obd); void mds_lov_set_cleanup_flags(struct obd_device *); int mds_lov_write_objids(struct obd_device *obd); void mds_lov_update_objids(struct obd_device *obd, obd_id *ids); -int mds_lov_set_growth(struct mds_obd *mds, int count); int mds_lov_set_nextid(struct obd_device *obd); int mds_lov_clearorphans(struct mds_obd *mds, struct obd_uuid *ost_uuid); int mds_post_mds_lovconf(struct obd_device *obd); @@ -162,6 +161,7 @@ int mds_query_write_access(struct inode *inode); int mds_open(struct mds_update_record *rec, int offset, struct ptlrpc_request *req, struct lustre_handle *); int mds_pin(struct ptlrpc_request *req); +void mds_mfd_unlink(struct mds_file_data *mfd, int decref); int mds_mfd_close(struct ptlrpc_request *req, struct obd_device *obd, struct mds_file_data *mfd, int unlink_orphan); int mds_close(struct ptlrpc_request *req); @@ -171,7 +171,7 @@ int mds_done_writing(struct ptlrpc_request *req); /* mds/mds_fs.c */ int mds_client_add(struct obd_device *obd, struct mds_obd *mds, struct mds_export_data *med, int cl_off); -int mds_client_free(struct obd_export *exp, int clear_client); +int mds_client_free(struct obd_export *exp); int mds_obd_create(struct obd_export *exp, struct obdo *oa, struct lov_stripe_md **ea, struct obd_trans_info *oti); int mds_obd_destroy(struct obd_export *exp, struct obdo *oa, @@ -193,7 +193,7 @@ void mds_pack_inode2body(struct mds_body *body, struct inode *inode); #endif /* mds/quota_master.c */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) && defined (HAVE_QUOTA_SUPPORT) int lustre_dquot_init(void); void lustre_dquot_exit(void); int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc); @@ -228,6 +228,49 @@ static inline int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) { return 0; } static inline int mds_get_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) { return 0; } -#endif /* KERNEL_VERSION(2,5,0) */ +#endif /* KERNEL_VERSION(2,5,0) && QUOTA */ + +#ifdef HAVE_QUOTA_SUPPORT +/* Internal quota stuff */ +int mds_quotacheck(struct ptlrpc_request *req); +int mds_quotactl(struct ptlrpc_request *req); +void mds_quota_setup(struct mds_obd *mds); +void mds_quota_cleanup(struct mds_obd *mds); +void mds_fs_quota_cleanup(struct mds_obd *mds); + +#ifdef LPROCFS +int lprocfs_mds_rd_bunit(char *page, char **start, off_t off, int count, + int *eof, void *data); +int lprocfs_mds_rd_iunit(char *page, char **start, off_t off, int count, + int *eof, void *data); +int lprocfs_mds_wr_bunit(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_mds_wr_iunit(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_mds_rd_btune(char *page, char **start, off_t off, int count, + int *eof, void *data); +int lprocfs_mds_rd_itune(char *page, char **start, off_t off, int count, + int *eof, void *data); +int lprocfs_mds_wr_btune(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_mds_wr_itune(struct file *file, const char *buffer, + unsigned long count, void *data); +#endif /* LPROCFS */ +#else /* QUOTA */ +static inline int mds_quotacheck(struct ptlrpc_request *req) +{ + req->rq_status = -EOPNOTSUPP; + return -EOPNOTSUPP; +} +static inline int mds_quotactl(struct ptlrpc_request *req) +{ + req->rq_status = -EOPNOTSUPP; + return -EOPNOTSUPP; +} +static inline void mds_quota_setup(struct mds_obd *mds) {} +static inline void mds_quota_cleanup(struct mds_obd *mds) {} +static inline void mds_fs_quota_cleanup(struct mds_obd *mds) {} +#endif /* Quota */ + #endif /* _MDS_INTERNAL_H */ diff --git a/lustre/mds/mds_lib.c b/lustre/mds/mds_lib.c index b4ea941..57dfb38 100644 --- a/lustre/mds/mds_lib.c +++ b/lustre/mds/mds_lib.c @@ -325,8 +325,7 @@ static update_unpacker mds_unpackers[REINT_MAX] = { int mds_update_unpack(struct ptlrpc_request *req, int offset, struct mds_update_record *rec) { - __u32 *opcodep; - __u32 opcode; + mds_reint_t opcode, *opcodep; int rc; ENTRY; @@ -348,5 +347,8 @@ int mds_update_unpack(struct ptlrpc_request *req, int offset, rec->ur_opcode = opcode; rc = mds_unpackers[opcode](req, offset, rec); +#if CRAY_PORTALS + rec->ur_fsuid = req->rq_uid; +#endif RETURN(rc); } diff --git a/lustre/mds/mds_log.c b/lustre/mds/mds_log.c index dc1a143..b73b7d2 100644 --- a/lustre/mds/mds_log.c +++ b/lustre/mds/mds_log.c @@ -43,10 +43,10 @@ static int mds_log_fill_unlink_rec(struct llog_rec_hdr *rec, void *data) { struct llog_fill_rec_data *lfd = (struct llog_fill_rec_data *)data; struct llog_unlink_rec *lur = (struct llog_unlink_rec *)rec; - + lur->lur_oid = lfd->lfd_id; lur->lur_ogen = lfd->lfd_ogen; - + RETURN(0); } @@ -55,10 +55,10 @@ static int mds_log_fill_setattr_rec(struct llog_rec_hdr *rec, void *data) { struct llog_fill_rec_data *lfd = (struct llog_fill_rec_data *)data; struct llog_setattr_rec *lsr = (struct llog_setattr_rec *)rec; - + lsr->lsr_oid = lfd->lfd_id; lsr->lsr_ogen = lfd->lfd_ogen; - + RETURN(0); } @@ -155,34 +155,33 @@ int mds_log_op_setattr(struct obd_device *obd, struct inode *inode, struct llog_setattr_rec *lsr; int rc; ENTRY; - + if (IS_ERR(mds->mds_osc_obd)) RETURN(PTR_ERR(mds->mds_osc_obd)); - - rc = obd_unpackmd(mds->mds_osc_exp, &lsm, - lmm, lmm_size); + + rc = obd_unpackmd(mds->mds_osc_exp, &lsm, lmm, lmm_size); if (rc < 0) RETURN(rc); OBD_ALLOC(lsr, sizeof(*lsr)); if (!lsr) - RETURN(-ENOMEM); - + GOTO(out, rc = -ENOMEM); + /* prepare setattr log record */ lsr->lsr_hdr.lrh_len = lsr->lsr_tail.lrt_len = sizeof(*lsr); lsr->lsr_hdr.lrh_type = MDS_SETATTR_REC; lsr->lsr_uid = inode->i_uid; lsr->lsr_gid = inode->i_gid; - + /* write setattr log */ ctxt = llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT); rc = llog_add(ctxt, &lsr->lsr_hdr, lsm, logcookies, cookies_size / sizeof(struct llog_cookie), mds_log_fill_setattr_rec); - obd_free_memmd(mds->mds_osc_exp, &lsm); OBD_FREE(lsr, sizeof(*lsr)); - + out: + obd_free_memmd(mds->mds_osc_exp, &lsm); RETURN(rc); } diff --git a/lustre/mds/mds_lov.c b/lustre/mds/mds_lov.c index 251a9c7..773165f 100644 --- a/lustre/mds/mds_lov.c +++ b/lustre/mds/mds_lov.c @@ -142,18 +142,6 @@ int mds_lov_set_nextid(struct obd_device *obd) RETURN(rc); } -/* tell the LOV-OSC by how much to pre-create */ -int mds_lov_set_growth(struct mds_obd *mds, int count) -{ - int rc; - ENTRY; - - rc = obd_set_info(mds->mds_osc_exp, strlen("growth_count"), - "growth_count", sizeof(count), &count); - - RETURN(rc); -} - int mds_lov_connect(struct obd_device *obd, char * lov_name) { struct mds_obd *mds = &obd->u.mds; @@ -305,7 +293,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, struct obd_device *obd = exp->exp_obd; struct mds_obd *mds = &obd->u.mds; struct obd_ioctl_data *data = karg; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc = 0; ENTRY; @@ -317,7 +305,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, if (mds->mds_cfg_llh) RETURN(-EBUSY); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = llog_create(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), &mds->mds_cfg_llh, NULL, name); if (rc == 0) @@ -325,7 +313,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, &cfg_uuid); else mds->mds_cfg_llh = NULL; - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -334,9 +322,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, if (!mds->mds_cfg_llh) RETURN(-EBADF); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = llog_close(mds->mds_cfg_llh); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); mds->mds_cfg_llh = NULL; RETURN(rc); @@ -347,7 +335,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, if (mds->mds_cfg_llh) RETURN(-EBUSY); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = llog_create(llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT), &mds->mds_cfg_llh, NULL, name); if (rc == 0) { @@ -357,7 +345,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, rc = llog_destroy(mds->mds_cfg_llh); llog_free_handle(mds->mds_cfg_llh); } - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); mds->mds_cfg_llh = NULL; RETURN(rc); @@ -387,10 +375,10 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, RETURN(rc); } - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = llog_write_rec(mds->mds_cfg_llh, &rec, NULL, 0, cfg_buf, -1); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); OBD_FREE(cfg_buf, data->ioc_plen1); RETURN(rc); @@ -399,9 +387,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, case OBD_IOC_PARSE: { struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = class_config_parse_llog(ctxt, data->ioc_inlbuf1, NULL); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc) RETURN(rc); @@ -411,9 +399,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, case OBD_IOC_DUMP_LOG: { struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = class_config_dump_llog(ctxt, data->ioc_inlbuf1, NULL); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc) RETURN(rc); @@ -430,7 +418,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, void *handle; struct inode *inode = obd->u.mds.mds_sb->s_root->d_inode; BDEVNAME_DECLARE_STORAGE(tmp); - CERROR("setting device %s read-only\n", + CERROR("*** setting device %s read-only ***\n", ll_bdevname(obd->u.mds.mds_sb, tmp)); handle = fsfilt_start(obd, inode, FSFILT_OP_MKNOD, NULL); @@ -440,7 +428,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, CDEBUG(D_HA, "syncing mds %s\n", obd->obd_name); rc = fsfilt_sync(obd, obd->u.mds.mds_sb); - ll_set_rdonly(ll_sbdev(obd->u.mds.mds_sb)); + lvfs_set_rdonly(lvfs_sbdev(obd->u.mds.mds_sb)); RETURN(0); } @@ -458,9 +446,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, int rc2; obd_llog_finish(obd, mds->mds_lov_desc.ld_tgt_count); - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); rc = llog_ioctl(ctxt, cmd, data); - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); llog_cat_initialize(obd, mds->mds_lov_desc.ld_tgt_count); rc2 = obd_set_info(mds->mds_osc_exp, strlen("mds_conn"), "mds_conn", 0, NULL); @@ -473,9 +461,9 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); rc = llog_ioctl(ctxt, cmd, data); - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -486,6 +474,7 @@ int mds_iocontrol(unsigned int cmd, struct obd_export *exp, int len, RETURN(0); default: + CDEBUG(D_INFO, "unknown command %x\n", cmd); RETURN(-EINVAL); } RETURN(0); @@ -503,7 +492,7 @@ int mds_lov_synchronize(void *data) struct obd_device *obd; struct obd_uuid *uuid; unsigned long flags; - int rc; + int rc = 0; lock_kernel(); ptlrpc_daemonize(); @@ -525,7 +514,7 @@ int mds_lov_synchronize(void *data) rc = obd_set_info(obd->u.mds.mds_osc_exp, strlen("mds_conn"), "mds_conn", 0, uuid); if (rc != 0) - RETURN(rc); + GOTO(out, rc); rc = llog_connect(llog_get_context(obd, LLOG_MDS_OST_ORIG_CTXT), obd->u.mds.mds_lov_desc.ld_tgt_count, @@ -533,7 +522,7 @@ int mds_lov_synchronize(void *data) if (rc != 0) { CERROR("%s: failed at llog_origin_connect: %d\n", obd->obd_name, rc); - RETURN(rc); + GOTO(out, rc); } CWARN("MDS %s: %s now active, resetting orphans\n", @@ -542,10 +531,12 @@ int mds_lov_synchronize(void *data) if (rc != 0) { CERROR("%s: failed at mds_lov_clearorphans: %d\n", obd->obd_name, rc); - RETURN(rc); + GOTO(out, rc); } - RETURN(0); +out: + class_export_put(obd->obd_self_export); + RETURN(rc); } int mds_lov_start_synchronize(struct obd_device *obd, struct obd_uuid *uuid) @@ -561,6 +552,9 @@ int mds_lov_start_synchronize(struct obd_device *obd, struct obd_uuid *uuid) mlsi->mlsi_obd = obd; mlsi->mlsi_uuid = uuid; + + /* We need to lock the mds in place for our new thread context. */ + class_export_get(obd->obd_self_export); rc = kernel_thread(mds_lov_synchronize, mlsi, CLONE_VM | CLONE_FILES); if (rc < 0) diff --git a/lustre/mds/mds_open.c b/lustre/mds/mds_open.c index b2b9d60..39cf431 100644 --- a/lustre/mds/mds_open.c +++ b/lustre/mds/mds_open.c @@ -68,6 +68,9 @@ static void mds_mfd_addref(void *mfdp) atomic_read(&mfd->mfd_refcount)); } +/* Create a new mds_file_data struct. + * One reference for handle+med_open_head list and dropped by mds_mfd_unlink(), + * one reference for the caller of this function. */ struct mds_file_data *mds_mfd_new(void) { struct mds_file_data *mfd; @@ -81,11 +84,14 @@ struct mds_file_data *mds_mfd_new(void) atomic_set(&mfd->mfd_refcount, 2); INIT_LIST_HEAD(&mfd->mfd_handle.h_link); + INIT_LIST_HEAD(&mfd->mfd_list); class_handle_hash(&mfd->mfd_handle, mds_mfd_addref); return mfd; } +/* Get a new reference on the mfd pointed to by handle, if handle is still + * valid. Caller must drop reference with mds_mfd_put(). */ static struct mds_file_data *mds_handle2mfd(struct lustre_handle *handle) { ENTRY; @@ -93,6 +99,7 @@ static struct mds_file_data *mds_handle2mfd(struct lustre_handle *handle) RETURN(class_handle2object(handle->cookie)); } +/* Drop mfd reference, freeing struct if this is the last one. */ static void mds_mfd_put(struct mds_file_data *mfd) { CDEBUG(D_INFO, "PUTting mfd %p : new refcount %d\n", mfd, @@ -105,13 +112,16 @@ static void mds_mfd_put(struct mds_file_data *mfd) } } -static void mds_mfd_destroy(struct mds_file_data *mfd) +/* Remove the mfd handle so that it cannot be found by open/close again. + * Caller must hold med_open_lock for mfd_list manipulation. */ +void mds_mfd_unlink(struct mds_file_data *mfd, int decref) { class_handle_unhash(&mfd->mfd_handle); - mds_mfd_put(mfd); + list_del_init(&mfd->mfd_list); + if (decref) + mds_mfd_put(mfd); } - /* Caller must hold mds->mds_epoch_sem */ static int mds_alloc_filterdata(struct inode *inode) { @@ -278,7 +288,6 @@ static struct mds_file_data *mds_dentry_open(struct dentry *dentry, spin_lock(&med->med_open_lock); list_add(&mfd->mfd_list, &med->med_open_head); spin_unlock(&med->med_open_lock); - mds_mfd_put(mfd); body->handle.cookie = mfd->mfd_handle.h_cookie; @@ -286,7 +295,7 @@ static struct mds_file_data *mds_dentry_open(struct dentry *dentry, cleanup_mfd: mds_mfd_put(mfd); - mds_mfd_destroy(mfd); + mds_mfd_unlink(mfd, 1); cleanup_dentry: return ERR_PTR(error); } @@ -295,9 +304,8 @@ cleanup_dentry: static int mds_create_objects(struct ptlrpc_request *req, int offset, struct mds_update_record *rec, struct mds_obd *mds, struct obd_device *obd, - struct dentry *dchild, void **handle, obd_id **ids, - struct llog_cookie **ret_logcookies, - int *setattr_async_flag) + struct dentry *dchild, void **handle, + obd_id **ids) { struct obdo *oa; struct obd_trans_info oti = { 0 }; @@ -358,6 +366,7 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset, RETURN(0); } + if (OBD_FAIL_CHECK_ONCE(OBD_FAIL_MDS_ALLOC_OBDO)) GOTO(out_ids, rc = -ENOMEM); @@ -415,7 +424,6 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset, } GOTO(out_oa, rc); } - *setattr_async_flag = 1; } else { rc = obd_iocontrol(OBD_IOC_LOV_SETEA, mds->mds_osc_exp, 0, &lsm, rec->ur_eadata); @@ -450,35 +458,14 @@ static int mds_create_objects(struct ptlrpc_request *req, int offset, lmm_size = rc; body->eadatasize = rc; - if (*handle == NULL) { - if (*setattr_async_flag) - *handle = fsfilt_start_log(obd, inode, - FSFILT_OP_CREATE, NULL, - le32_to_cpu(lmm->lmm_stripe_count)); - else - *handle = fsfilt_start(obd, inode, FSFILT_OP_CREATE, NULL); - } + if (*handle == NULL) + *handle = fsfilt_start(obd, inode, FSFILT_OP_CREATE, NULL); if (IS_ERR(*handle)) { rc = PTR_ERR(*handle); *handle = NULL; GOTO(out_oa, rc); } - /* write mds setattr log for created objects */ - if (*setattr_async_flag && lmm_size) { - struct llog_cookie *logcookies = NULL; - - OBD_ALLOC(logcookies, mds->mds_max_cookiesize); - if (logcookies == NULL) - GOTO(out_oa, rc = -ENOMEM); - *ret_logcookies = logcookies; - if (mds_log_op_setattr(obd, inode, lmm, lmm_size, logcookies, - mds->mds_max_cookiesize) <= 0) { - OBD_FREE(logcookies, mds->mds_max_cookiesize); - *ret_logcookies = NULL; - } - } - rc = fsfilt_set_md(obd, inode, *handle, lmm, lmm_size); lmm_buf = lustre_msg_buf(req->rq_repmsg, offset, 0); lmm_bufsize = req->rq_repmsg->buflens[offset]; @@ -586,12 +573,16 @@ static void reconstruct_open(struct mds_update_record *rec, int offset, GOTO(out_dput, 0); mfd = NULL; + spin_lock(&med->med_open_lock); list_for_each(t, &med->med_open_head) { mfd = list_entry(t, struct mds_file_data, mfd_list); - if (mfd->mfd_xid == req->rq_xid) + if (mfd->mfd_xid == req->rq_xid) { + mds_mfd_addref(mfd); break; + } mfd = NULL; } + spin_unlock(&med->med_open_lock); /* #warning "XXX fixme" bug 2991 */ /* Here it used to LASSERT(mfd) if exp_outstanding_reply != NULL. @@ -613,6 +604,8 @@ static void reconstruct_open(struct mds_update_record *rec, int offset, mfd->mfd_handle.h_cookie); } + mds_mfd_put(mfd); + out_dput: if (put_child) l_dput(dchild); @@ -645,9 +638,7 @@ static int accmode(struct inode *inode, int flags) /* Handles object creation, actual opening, and I/O epoch */ static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild, struct mds_body *body, int flags, void **handle, - struct mds_update_record *rec,struct ldlm_reply *rep, - struct llog_cookie **logcookies, - int *setattr_async_flag) + struct mds_update_record *rec,struct ldlm_reply *rep) { struct mds_obd *mds = mds_req2mds(req); struct obd_device *obd = req->rq_export->exp_obd; @@ -677,8 +668,7 @@ static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild, if (!(body->valid & OBD_MD_FLEASIZE)) { /* no EA: create objects */ rc = mds_create_objects(req, 2, rec, mds, obd, - dchild, handle, &ids, - logcookies, setattr_async_flag); + dchild, handle, &ids); if (rc) { CERROR("mds_create_objects: rc = %d\n", rc); up(&dchild->d_inode->i_sem); @@ -701,12 +691,14 @@ static int mds_finish_open(struct ptlrpc_request *req, struct dentry *dchild, CDEBUG(D_INODE, "mfd %p, cookie "LPX64"\n", mfd, mfd->mfd_handle.h_cookie); + if (ids != NULL) { mds_lov_update_objids(obd, ids); OBD_FREE(ids, sizeof(*ids) * mds->mds_lov_desc.ld_tgt_count); } - //if (rc) - // mds_mfd_destroy(mfd); + if (rc) + mds_mfd_unlink(mfd, 1); + mds_mfd_put(mfd); RETURN(rc); } @@ -714,16 +706,11 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid, struct mds_body *body, int flags, struct mds_update_record *rec,struct ldlm_reply *rep) { - struct obd_device *obd = req->rq_export->exp_obd; struct mds_obd *mds = mds_req2mds(req); struct dentry *dchild; char fidname[LL_FID_NAMELEN]; int fidlen = 0, rc; void *handle = NULL; - struct llog_cookie *logcookies = NULL; - struct lov_mds_md *lmm = NULL; - int lmm_size = 0; - int setattr_async_flag = 0; ENTRY; fidlen = ll_fid2str(fidname, fid->id, fid->generation); @@ -758,17 +745,9 @@ static int mds_open_by_fid(struct ptlrpc_request *req, struct ll_fid *fid, intent_set_disposition(rep, DISP_LOOKUP_POS); open: - rc = mds_finish_open(req, dchild, body, flags, &handle, rec, rep, - &logcookies, &setattr_async_flag); + rc = mds_finish_open(req, dchild, body, flags, &handle, rec, rep); rc = mds_finish_transno(mds, dchild ? dchild->d_inode : NULL, handle, req, rc, rep ? rep->lock_policy_res1 : 0); - /* do mds to ost setattr for new created objects */ - if (rc == 0 && setattr_async_flag) { - lmm = lustre_msg_buf(req->rq_repmsg, 2, 0); - lmm_size = req->rq_repmsg->buflens[2]; - rc = mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size, - logcookies); - } /* XXX what do we do here if mds_finish_transno itself failed? */ l_dput(dchild); @@ -779,7 +758,7 @@ int mds_pin(struct ptlrpc_request *req) { struct obd_device *obd = req->rq_export->exp_obd; struct mds_body *request_body, *reply_body; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc, size = sizeof(*reply_body); ENTRY; @@ -790,10 +769,10 @@ int mds_pin(struct ptlrpc_request *req) RETURN(rc); reply_body = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*reply_body)); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = mds_open_by_fid(req, &request_body->fid1, reply_body, request_body->flags, NULL, NULL); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -839,10 +818,6 @@ int mds_open(struct mds_update_record *rec, int offset, int parent_mode = LCK_PR; void *handle = NULL; struct dentry_params dp; - struct lov_mds_md *lmm = NULL; - int lmm_size = 0; - struct llog_cookie *logcookies = NULL; - int setattr_async_flag = 0; uid_t parent_uid = 0; gid_t parent_gid = 0; ENTRY; @@ -1073,19 +1048,12 @@ int mds_open(struct mds_update_record *rec, int offset, /* Step 5: mds_open it */ rc = mds_finish_open(req, dchild, body, rec->ur_flags, &handle, rec, - rep, &logcookies, &setattr_async_flag); + rep); GOTO(cleanup, rc); cleanup: rc = mds_finish_transno(mds, dchild ? dchild->d_inode : NULL, handle, req, rc, rep ? rep->lock_policy_res1 : 0); - /* do mds to ost setattr for new created objects */ - if (rc == 0 && setattr_async_flag) { - lmm = lustre_msg_buf(req->rq_repmsg, 2, 0); - lmm_size = req->rq_repmsg->buflens[2]; - mds_osc_setattr_async(obd, dchild->d_inode, lmm, lmm_size, - logcookies); - } cleanup_no_trans: switch (cleanup_phase) { @@ -1115,7 +1083,7 @@ int mds_open(struct mds_update_record *rec, int offset, else ptlrpc_save_lock (req, &parent_lockh, parent_mode); } - + /* trigger dqacq on the owner of child and parent */ mds_adjust_qunit(obd, current->fsuid, current->fsgid, parent_uid, parent_gid, rc); @@ -1289,7 +1257,7 @@ out: if (rc > 0) rc = 0; l_dput(mfd->mfd_dentry); - mds_mfd_destroy(mfd); + mds_mfd_put(mfd); cleanup: if (req != NULL && reply_body != NULL) { @@ -1318,7 +1286,7 @@ int mds_close(struct ptlrpc_request *req) struct obd_device *obd = req->rq_export->exp_obd; struct mds_body *body; struct mds_file_data *mfd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct inode *inode; int rc, repsize[3] = {sizeof(struct mds_body), obd->u.mds.mds_max_mdsize, @@ -1343,13 +1311,19 @@ int mds_close(struct ptlrpc_request *req) if (body->flags & MDS_BFLAG_UNCOMMITTED_WRITES) /* do some stuff */ ; + spin_lock(&med->med_open_lock); mfd = mds_handle2mfd(&body->handle); if (mfd == NULL) { + spin_unlock(&med->med_open_lock); DEBUG_REQ(D_ERROR, req, "no handle for file close ino "LPD64 ": cookie "LPX64, body->fid1.id, body->handle.cookie); req->rq_status = -ESTALE; RETURN(-ESTALE); } + /* Remove mfd handle so it can't be found again. We consume mfd_list + * reference here, but still have mds_handle2mfd ref until mfd_close. */ + mds_mfd_unlink(mfd, 1); + spin_unlock(&med->med_open_lock); inode = mfd->mfd_dentry->d_inode; /* child orphan sem protects orphan_dec_test && is_orphan race */ @@ -1362,15 +1336,11 @@ int mds_close(struct ptlrpc_request *req) mds_pack_inode2body(body, inode); mds_pack_md(obd, req->rq_repmsg, 1,body,inode,MDS_PACK_MD_LOCK); } - spin_lock(&med->med_open_lock); - list_del(&mfd->mfd_list); - spin_unlock(&med->med_open_lock); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); req->rq_status = mds_mfd_close(req, obd, mfd, 1); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); - mds_mfd_put(mfd); if (OBD_FAIL_CHECK(OBD_FAIL_MDS_CLOSE_PACK)) { CERROR("test case OBD_FAIL_MDS_CLOSE_PACK\n"); req->rq_status = -ENOMEM; diff --git a/lustre/mds/mds_reint.c b/lustre/mds/mds_reint.c index 1d54b6d..d163220 100644 --- a/lustre/mds/mds_reint.c +++ b/lustre/mds/mds_reint.c @@ -126,7 +126,7 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle, } } - off = med->med_off; + off = med->med_lr_off; transno = req->rq_reqmsg->transno; if (rc != 0) { @@ -147,10 +147,15 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle, mcd->mcd_last_result = cpu_to_le32(rc); mcd->mcd_last_data = cpu_to_le32(op_data); - fsfilt_add_journal_cb(req->rq_export->exp_obd, transno, handle, - mds_commit_cb, NULL); - err = fsfilt_write_record(obd, mds->mds_rcvd_filp, mcd, sizeof(*mcd), - &off, 0); + if (off <= 0) { + CERROR("client idx %d has offset %lld\n", med->med_lr_idx, off); + err = -EINVAL; + } else { + fsfilt_add_journal_cb(req->rq_export->exp_obd, transno, handle, + mds_commit_cb, NULL); + err = fsfilt_write_record(obd, mds->mds_rcvd_filp, mcd, + sizeof(*mcd), &off, 0); + } if (err) { log_pri = D_ERROR; @@ -160,7 +165,7 @@ int mds_finish_transno(struct mds_obd *mds, struct inode *inode, void *handle, DEBUG_REQ(log_pri, req, "wrote trans #"LPU64" rc %d client %s at idx %u: err = %d", - transno, rc, mcd->mcd_uuid, med->med_idx, err); + transno, rc, mcd->mcd_uuid, med->med_lr_idx, err); err = mds_lov_write_objids(obd); if (err) { @@ -370,11 +375,11 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode, struct lov_stripe_md *lsm = NULL; struct obd_trans_info oti = { 0 }; struct obdo *oa = NULL; - int cleanup_phase = 0, rc = 0; + int rc; ENTRY; if (OBD_FAIL_CHECK(OBD_FAIL_MDS_OST_SETATTR)) - GOTO(cleanup, rc); + RETURN(0); /* first get memory EA */ oa = obdo_alloc(); @@ -383,14 +388,12 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode, LASSERT(lmm); - cleanup_phase = 1; rc = obd_unpackmd(mds->mds_osc_exp, &lsm, lmm, lmm_size); if (rc < 0) { CERROR("Error unpack md %p\n", lmm); - GOTO(cleanup, rc); + GOTO(out, rc); } - - cleanup_phase = 2; + /* then fill oa */ oa->o_id = lsm->lsm_object_id; oa->o_uid = inode->i_uid; @@ -400,23 +403,16 @@ int mds_osc_setattr_async(struct obd_device *obd, struct inode *inode, oa->o_valid |= OBD_MD_FLCOOKIE; oti.oti_logcookies = logcookies; } - + /* do setattr from mds to ost asynchronously */ rc = obd_setattr_async(mds->mds_osc_exp, oa, lsm, &oti); if (rc) - CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64" on ost error " - "%d\n", lsm->lsm_object_id, rc); -cleanup: - switch(cleanup_phase) { - case 2: - obd_free_memmd(mds->mds_osc_exp, &lsm); - case 1: - obdo_free(oa); - case 0: - if (logcookies) - OBD_FREE(logcookies, mds->mds_max_cookiesize); - } - + CDEBUG(D_INODE, "mds to ost setattr objid 0x"LPX64 + " on ost error %d\n", lsm->lsm_object_id, rc); + + obd_free_memmd(mds->mds_osc_exp, &lsm); + out: + obdo_free(oa); RETURN(rc); } @@ -483,17 +479,17 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset, OBD_FAIL_WRITE(OBD_FAIL_MDS_REINT_SETATTR_WRITE, inode->i_sb); - /* start a log jounal handle if needed*/ + /* start a log jounal handle if needed */ if (S_ISREG(inode->i_mode) && rec->ur_iattr.ia_valid & (ATTR_UID | ATTR_GID)) { lmm_size = mds->mds_max_mdsize; OBD_ALLOC(lmm, lmm_size); if (lmm == NULL) GOTO(cleanup, rc = -ENOMEM); - + cleanup_phase = 2; rc = mds_get_md(obd, inode, lmm, &lmm_size, need_lock); - if (rc < 0) + if (rc < 0) GOTO(cleanup, rc); handle = fsfilt_start_log(obd, inode, FSFILT_OP_SETATTR, NULL, @@ -523,6 +519,7 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset, OBD_ALLOC(logcookies, mds->mds_max_cookiesize); if (logcookies == NULL) GOTO(cleanup, rc = -ENOMEM); + if (mds_log_op_setattr(obd, inode, lmm, lmm_size, logcookies, mds->mds_max_cookiesize) <= 0) { @@ -542,20 +539,19 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset, GOTO(cleanup, rc); lum = rec->ur_eadata; - /* if lmm_stripe_size is -1, then delete the stripe - info from the dir */ - if (S_ISDIR(inode->i_mode) && + /* if lmm_stripe_size is -1 then delete stripe info from dir */ + if (S_ISDIR(inode->i_mode) && lum->lmm_stripe_size == (typeof(lum->lmm_stripe_size))(-1)){ rc = fsfilt_set_md(obd, inode, handle, NULL, 0); if (rc) GOTO(cleanup, rc); } else { rc = obd_iocontrol(OBD_IOC_LOV_SETSTRIPE, - mds->mds_osc_exp, 0, + mds->mds_osc_exp, 0, &lsm, rec->ur_eadata); if (rc) GOTO(cleanup, rc); - + obd_free_memmd(mds->mds_osc_exp, &lsm); rc = fsfilt_set_md(obd, inode, handle, rec->ur_eadata, @@ -602,12 +598,14 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset, mds_cancel_cookies_cb, mlcd); err = mds_finish_transno(mds, inode, handle, req, rc, 0); /* do mds to ost setattr if needed */ - if (!rc && !err && lmm_size) + if (!rc && !err && lmm_size) mds_osc_setattr_async(obd, inode, lmm, lmm_size, logcookies); switch (cleanup_phase) { case 2: OBD_FREE(lmm, mds->mds_max_mdsize); + if (logcookies) + OBD_FREE(logcookies, mds->mds_max_cookiesize); case 1: if ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) && rec->ur_eadata != NULL) @@ -632,7 +630,7 @@ static int mds_reint_setattr(struct mds_update_record *rec, int offset, /* trigger dqrel/dqacq for original owner and new owner */ if (rec->ur_iattr.ia_valid & (ATTR_UID | ATTR_GID)) { - mds_adjust_qunit(obd, rec->ur_iattr.ia_uid, + mds_adjust_qunit(obd, rec->ur_iattr.ia_uid, rec->ur_iattr.ia_gid, 0, 0, rc); mds_adjust_qunit(obd, child_uid, child_gid, 0, 0, rc); } @@ -698,7 +696,8 @@ static int mds_reint_create(struct mds_update_record *rec, int offset, if (IS_ERR(dparent)) { rc = PTR_ERR(dparent); if (rc != -ENOENT) - CERROR("parent lookup error %d\n", rc); + CERROR("parent "LPU64"/%u lookup error %d\n", + rec->ur_fid1->id, rec->ur_fid1->generation, rc); GOTO(cleanup, rc); } cleanup_phase = 1; /* locked parent dentry */ @@ -810,24 +809,8 @@ static int mds_reint_create(struct mds_update_record *rec, int offset, CDEBUG(D_INODE, "recreated ino %lu with gen %u\n", inode->i_ino, inode->i_generation); } else { - struct lustre_handle child_ino_lockh; - CDEBUG(D_INODE, "created ino %lu with gen %x\n", inode->i_ino, inode->i_generation); - - /* The inode we were allocated may have just been freed - * by an unlink operation. We take this lock to - * synchronize against the matching reply-ack-lock taken - * in unlink, to avoid replay problems if this reply - * makes it out to the client but the unlink's does not. - * See bug 2029 for more detail.*/ - rc = mds_lock_new_child(obd, inode, &child_ino_lockh); - if (rc != ELDLM_OK) { - CERROR("error locking for unlink/create sync: " - "%d\n", rc); - } else { - ldlm_lock_decref(&child_ino_lockh, LCK_EX); - } } rc = fsfilt_setattr(obd, dchild, handle, &iattr, 0); @@ -845,12 +828,12 @@ static int mds_reint_create(struct mds_update_record *rec, int offset, rc = mds_get_md(obd, dir, &lmm, &lmm_size, 1); if (rc > 0) { down(&inode->i_sem); - rc = fsfilt_set_md(obd, inode, handle, + rc = fsfilt_set_md(obd, inode, handle, &lmm, lmm_size); up(&inode->i_sem); } if (rc) - CERROR("error on copy stripe info: rc = %d\n", + CERROR("error on copy stripe info: rc = %d\n", rc); } @@ -882,6 +865,13 @@ cleanup: break; } } else if (created) { + /* The inode we were allocated may have just been freed + * by an unlink operation. We take this lock to + * synchronize against the matching reply-ack-lock taken + * in unlink, to avoid replay problems if this reply + * makes it out to the client but the unlink's does not. + * See bug 2029 for more detail.*/ + mds_lock_new_child(obd, dchild->d_inode, NULL); /* save uid/gid of create inode and parent */ parent_uid = dir->i_uid; parent_gid = dir->i_gid; @@ -906,9 +896,9 @@ cleanup: LBUG(); } req->rq_status = rc; - + /* trigger dqacq on the owner of child and parent */ - mds_adjust_qunit(obd, current->fsuid, current->fsgid, + mds_adjust_qunit(obd, current->fsuid, current->fsgid, parent_uid, parent_gid, rc); return 0; } @@ -1401,7 +1391,7 @@ static int mds_reint_unlink(struct mds_update_record *rec, int offset, child_gid = child_inode->i_gid; parent_uid = dparent->d_inode->i_uid; parent_gid = dparent->d_inode->i_gid; - + cleanup_phase = 2; /* dchild has a lock */ /* We have to do these checks ourselves, in case we are making an @@ -2082,16 +2072,16 @@ int mds_reint_rec(struct mds_update_record *rec, int offset, struct ptlrpc_request *req, struct lustre_handle *lockh) { struct obd_device *obd = req->rq_export->exp_obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc; ENTRY; /* checked by unpacker */ LASSERT(rec->ur_opcode < REINT_MAX && reinters[rec->ur_opcode] != NULL); - push_ctxt(&saved, &obd->obd_ctxt, &rec->ur_uc); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, &rec->ur_uc); rc = reinters[rec->ur_opcode] (rec, offset, req, lockh); - pop_ctxt(&saved, &obd->obd_ctxt, &rec->ur_uc); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, &rec->ur_uc); RETURN(rc); } diff --git a/lustre/mds/mds_unlink_open.c b/lustre/mds/mds_unlink_open.c index 737299c..6298eb4 100644 --- a/lustre/mds/mds_unlink_open.c +++ b/lustre/mds/mds_unlink_open.c @@ -129,10 +129,10 @@ static int mds_unlink_orphan(struct obd_device *obd, struct dentry *dchild, } rc = vfs_unlink(pending_dir, dchild); - if (rc) + if (rc) { CERROR("error %d unlinking orphan %.*s from PENDING\n", rc, dchild->d_name.len, dchild->d_name.name); - else if (lmm_size) { + } else if (lmm_size) { OBD_ALLOC(logcookies, mds->mds_max_cookiesize); if (logcookies == NULL) rc = -ENOMEM; @@ -161,7 +161,7 @@ out_free_lmm: int mds_cleanup_orphans(struct obd_device *obd) { struct mds_obd *mds = &obd->u.mds; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct file *file; struct dentry *dchild, *dentry; struct vfsmount *mnt; @@ -173,7 +173,7 @@ int mds_cleanup_orphans(struct obd_device *obd) int i = 0, rc = 0, item = 0, namlen; ENTRY; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); dentry = dget(mds->mds_pending_dir); if (IS_ERR(dentry)) GOTO(err_pop, rc = PTR_ERR(dentry)); @@ -219,6 +219,13 @@ int mds_cleanup_orphans(struct obd_device *obd) GOTO(next, rc = 0); } + if (is_bad_inode(dchild->d_inode)) { + CERROR("bad orphan inode found %lu/%u\n", + dchild->d_inode->i_ino, + dchild->d_inode->i_generation); + GOTO(next, rc = -ENOENT); + } + child_inode = dchild->d_inode; MDS_DOWN_READ_ORPHAN_SEM(child_inode); if (mds_inode_is_orphan(child_inode) && @@ -248,7 +255,7 @@ err_out: OBD_FREE(dirent, sizeof(*dirent)); } err_pop: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc == 0) rc = item; RETURN(rc); diff --git a/lustre/mds/quota_context.c b/lustre/mds/quota_context.c index 907255a..1672bb2 100644 --- a/lustre/mds/quota_context.c +++ b/lustre/mds/quota_context.c @@ -9,18 +9,8 @@ * * This file is part of Lustre, http://www.lustre.org. * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. + * No redistribution or use is permitted outside of Cluster File Systems, Inc. * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef EXPORT_SYMTAB # define EXPORT_SYMTAB @@ -442,8 +432,10 @@ schedule_dqacq(struct obd_device *obd, /* build dqacq/dqrel request */ LASSERT(qctxt->lqc_import); req = ptlrpc_prep_req(qctxt->lqc_import, opc, 1, &size, NULL); - if (!req) + if (!req) { + dqacq_completion(obd, qctxt, qdata, -ENOMEM, opc); RETURN(-ENOMEM); + } reqdata = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*reqdata)); memcpy(reqdata, qdata, sizeof(*reqdata)); @@ -470,7 +462,7 @@ wait_completion: if (qw.qw_rc == 0) rc = -EAGAIN; - QDATA_DEBUG(p, "wait dqacq done. (rc:%d)\n", qw.qw_rc); + CDEBUG(D_QUOTA, "wait dqacq done. (rc:%d)\n", qw.qw_rc); } RETURN(rc); } @@ -510,6 +502,7 @@ next: RETURN(rc); } +EXPORT_SYMBOL(qctxt_adjust_qunit); int qctxt_wait_on_dqacq(struct obd_device *obd, struct lustre_quota_ctxt *qctxt, @@ -541,6 +534,7 @@ next: RETURN(rc); } +EXPORT_SYMBOL(qctxt_wait_on_dqacq); int qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb, @@ -564,6 +558,7 @@ qctxt_init(struct lustre_quota_ctxt *qctxt, struct super_block *sb, RETURN(0); } +EXPORT_SYMBOL(qctxt_init); void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force) { @@ -586,3 +581,4 @@ void qctxt_cleanup(struct lustre_quota_ctxt *qctxt, int force) spin_unlock(&qunit_hash_lock); EXIT; } +EXPORT_SYMBOL(qctxt_cleanup); diff --git a/lustre/mds/quota_master.c b/lustre/mds/quota_master.c index e7ca633..36c27e0 100644 --- a/lustre/mds/quota_master.c +++ b/lustre/mds/quota_master.c @@ -9,18 +9,8 @@ * * This file is part of Lustre, http://www.lustre.org. * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. + * No redistribution or use is permitted outside of Cluster File Systems, Inc. * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef EXPORT_SYMTAB # define EXPORT_SYMTAB @@ -229,10 +219,15 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc) struct lustre_quota_info *info = &mds->mds_quota_info; struct lustre_dquot *dquot = NULL; __u64 *usage = NULL; - __u32 *limit = NULL; + __u32 hlimit = 0, slimit = 0; + time_t *time = NULL; + unsigned int grace = 0; int rc = 0; ENTRY; + /* slaves never acquires qunit for user root */ + LASSERT(qdata->qd_id || qdata->qd_type == GRPQUOTA); + dquot = lustre_dqget(obd, info, qdata->qd_id, qdata->qd_type); if (IS_ERR(dquot)) RETURN(PTR_ERR(dquot)); @@ -244,27 +239,48 @@ int dqacq_handler(struct obd_device *obd, struct qunit_data *qdata, int opc) down(&dquot->dq_sem); if (qdata->qd_isblk) { + grace = info->qi_info[qdata->qd_type].dqi_bgrace; usage = &dquot->dq_dqb.dqb_curspace; - limit = &dquot->dq_dqb.dqb_bhardlimit; + hlimit = dquot->dq_dqb.dqb_bhardlimit; + slimit = dquot->dq_dqb.dqb_bsoftlimit; + time = &dquot->dq_dqb.dqb_btime; } else { + grace = info->qi_info[qdata->qd_type].dqi_igrace; usage = (__u64 *) & dquot->dq_dqb.dqb_curinodes; - limit = &dquot->dq_dqb.dqb_ihardlimit; - } + hlimit = dquot->dq_dqb.dqb_ihardlimit; + slimit = dquot->dq_dqb.dqb_isoftlimit; + time = &dquot->dq_dqb.dqb_itime; + } /* if the quota limit in admin quotafile is zero, we just inform * slave to clear quota limit with zero qd_count */ - if (*limit == 0) { + if (hlimit == 0 && slimit == 0) { qdata->qd_count = 0; GOTO(out, rc); } + if (opc == QUOTA_DQACQ) { - if (QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > *limit) + if (hlimit && + QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > hlimit) GOTO(out, rc = -EDQUOT); - else - *usage += qdata->qd_count; + + if (slimit && + QUSG(*usage + qdata->qd_count, qdata->qd_isblk) > slimit) { + if (*time && CURRENT_SECONDS >= *time) + GOTO(out, rc = -EDQUOT); + else if (!*time) + *time = CURRENT_SECONDS + grace; + } + + *usage += qdata->qd_count; + } else if (opc == QUOTA_DQREL) { LASSERT(*usage - qdata->qd_count >= 0); *usage -= qdata->qd_count; + + /* (usage <= soft limit) but not (usage < soft limit) */ + if (!slimit || QUSG(*usage, qdata->qd_isblk) <= slimit) + *time = 0; } else { LBUG(); } @@ -304,7 +320,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl) struct mds_obd *mds = &obd->u.mds; struct lustre_quota_info *qinfo = &mds->mds_quota_info; const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; char name[64]; int i, rc = 0; struct dentry *dparent = mds->mds_objects_dir; @@ -312,7 +328,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl) ENTRY; LASSERT(iparent); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); down(&qinfo->qi_sem); for (i = 0; i < MAXQUOTAS; i++) { @@ -374,7 +390,7 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl) } up(&qinfo->qi_sem); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -383,14 +399,14 @@ int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl) struct mds_obd *mds = &obd->u.mds; struct lustre_quota_info *qinfo = &mds->mds_quota_info; const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; char name[64]; int i, rc = 0; struct inode *iparent = mds->mds_objects_dir->d_inode; ENTRY; LASSERT(iparent); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); down(&qinfo->qi_sem); /* open admin quota files and read quotafile info */ @@ -424,7 +440,7 @@ int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl) } up(&qinfo->qi_sem); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc && rc != -EBUSY) { down(&qinfo->qi_sem); @@ -517,7 +533,7 @@ static int mds_init_slave_ilimits(struct obd_device *obd, ENTRY; /* if we are going to set zero limit, needn't init slaves */ - if (!oqctl->qc_dqblk.dqb_ihardlimit) + if (!oqctl->qc_dqblk.dqb_ihardlimit && !oqctl->qc_dqblk.dqb_isoftlimit) RETURN(0); OBD_ALLOC(ioqc, sizeof(*ioqc)); @@ -562,7 +578,7 @@ static int mds_init_slave_blimits(struct obd_device *obd, ENTRY; /* if we are going to set zero limit, needn't init slaves */ - if (!oqctl->qc_dqblk.dqb_bhardlimit) + if (!oqctl->qc_dqblk.dqb_bhardlimit && !oqctl->qc_dqblk.dqb_bsoftlimit) RETURN(0); OBD_ALLOC(ioqc, sizeof(*ioqc)); @@ -634,7 +650,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) dquot->dq_dqb.dqb_bhardlimit = dqblk->dqb_bhardlimit; dquot->dq_dqb.dqb_bsoftlimit = dqblk->dqb_bsoftlimit; /* clear usage (limit pool) */ - if (dquot->dq_dqb.dqb_bhardlimit == 0) + if (!dquot->dq_dqb.dqb_bhardlimit && + !dquot->dq_dqb.dqb_bsoftlimit) dquot->dq_dqb.dqb_curspace = 0; } @@ -642,7 +659,8 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) dquot->dq_dqb.dqb_ihardlimit = dqblk->dqb_ihardlimit; dquot->dq_dqb.dqb_isoftlimit = dqblk->dqb_isoftlimit; /* clear usage (limit pool) */ - if (dquot->dq_dqb.dqb_ihardlimit == 0) + if (!dquot->dq_dqb.dqb_ihardlimit && + !dquot->dq_dqb.dqb_isoftlimit) dquot->dq_dqb.dqb_curinodes = 0; } @@ -660,7 +678,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) if (rc) GOTO(out, rc); - if (dqblk->dqb_valid & QIF_ILIMITS && !ihardlimit) { + if (dqblk->dqb_valid & QIF_ILIMITS && !(ihardlimit || isoftlimit)) { rc = mds_init_slave_ilimits(obd, oqctl); if (rc) { CERROR("init slave ilimits failed! (rc:%d)\n", rc); @@ -668,7 +686,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) } } - if (dqblk->dqb_valid & QIF_BLIMITS && !bhardlimit) { + if (dqblk->dqb_valid & QIF_BLIMITS && !(bhardlimit || bsoftlimit)) { rc = mds_init_slave_blimits(obd, oqctl); if (rc) { CERROR("init slave blimits failed! (rc:%d)\n", rc); diff --git a/lustre/obdclass/autoMakefile.am b/lustre/obdclass/autoMakefile.am index 8d670dc..0cdce91 100644 --- a/lustre/obdclass/autoMakefile.am +++ b/lustre/obdclass/autoMakefile.am @@ -15,5 +15,5 @@ modulefs_DATA = obdclass$(KMODEXT) noinst_DATA = llog_test$(KMODEXT) endif # MODULES -MOSTLYCLEANFILES = *.o *.ko *.mod.c llog-test.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ llog-test.c DIST_SOURCES = $(filter-out llog-test.c,$(obdclass-objs:.o=.c)) $(llog-test-objs:.o=.c) llog_test.c llog_internal.h diff --git a/lustre/obdclass/class_obd.c b/lustre/obdclass/class_obd.c index 28613e3..f4bb525 100644 --- a/lustre/obdclass/class_obd.c +++ b/lustre/obdclass/class_obd.c @@ -89,7 +89,7 @@ int proc_version; unsigned int obd_fail_loc; unsigned int obd_dump_on_timeout; unsigned int obd_timeout = 100; /* seconds */ -unsigned int ldlm_timeout = 6; /* seconds */ +unsigned int ldlm_timeout = 20; /* seconds */ char obd_lustre_upcall[128] = "DEFAULT"; /* or NONE or /full/path/to/upcall */ unsigned int obd_sync_filter; /* = 0, don't sync by default */ @@ -102,33 +102,9 @@ unsigned int obd_print_fail_loc(void) return obd_fail_loc; } -void ll_set_rdonly(ll_sbdev_type dev) +void obd_set_fail_loc(unsigned int fl) { - CDEBUG(D_IOCTL | D_HA, "set dev %ld rdonly\n", (long)dev); - ll_sbdev_sync(dev); -#ifdef HAVE_OLD_DEV_SET_RDONLY - dev_set_rdonly(dev, 2); -#else - dev_set_rdonly(dev); -#endif -} - -void ll_clear_rdonly(ll_sbdev_type dev) -{ -#ifndef HAVE_CLEAR_RDONLY_ON_PUT - CDEBUG(D_IOCTL | D_HA, "unset dev %ld rdonly\n", (long)dev); - if (ll_check_rdonly(dev)) { - ll_sbdev_sync(dev); -#ifdef HAVE_OLD_DEV_SET_RDONLY - dev_clear_rdonly(2); -#else - dev_clear_rdonly(dev); -#endif - } -#else - CDEBUG(D_IOCTL | D_HA, "(will unset dev %ld rdonly on put)\n", - (long)dev); -#endif + obd_fail_loc = fl; } /* opening /dev/obd */ @@ -234,13 +210,12 @@ int class_handle_ioctl(unsigned int cmd, unsigned long arg) data->ioc_plen1); if (err) GOTO(out, err); - + OBD_ALLOC(lcfg, data->ioc_plen1); err = copy_from_user(lcfg, data->ioc_pbuf1, data->ioc_plen1); - if (err) - GOTO(out, err); - err = class_process_config(lcfg); + if (!err) + err = class_process_config(lcfg); OBD_FREE(lcfg, data->ioc_plen1); GOTO(out, err); } @@ -330,7 +305,7 @@ int class_handle_ioctl(unsigned int cmd, unsigned long arg) if (data->ioc_dev >= MAX_OBD_DEVICES) { CERROR("OBD ioctl: No device\n"); GOTO(out, err = -EINVAL); - } + } obd = &obd_dev[data->ioc_dev]; if (!(obd && obd->obd_set_up) || obd->obd_stopping) { CERROR("OBD ioctl: device not setup %d \n", data->ioc_dev); @@ -390,7 +365,7 @@ static struct file_operations obd_psdev_fops = { /* modules setup */ static struct miscdevice obd_psdev = { .minor = OBD_MINOR, - .name = "obd_psdev", + .name = "obd", .fops = &obd_psdev_fops, }; #else @@ -398,13 +373,7 @@ void *obd_psdev = NULL; #endif EXPORT_SYMBOL(obd_dev); -EXPORT_SYMBOL(obdo_cachep); -EXPORT_SYMBOL(qunit_cachep); -EXPORT_SYMBOL(qunit_hash_lock); -EXPORT_SYMBOL(qunit_hash); EXPORT_SYMBOL(obd_fail_loc); -EXPORT_SYMBOL(ll_set_rdonly); -EXPORT_SYMBOL(ll_clear_rdonly); EXPORT_SYMBOL(obd_print_fail_loc); EXPORT_SYMBOL(obd_race_waitq); EXPORT_SYMBOL(obd_dump_on_timeout); @@ -414,6 +383,8 @@ EXPORT_SYMBOL(obd_lustre_upcall); EXPORT_SYMBOL(obd_sync_filter); EXPORT_SYMBOL(ptlrpc_put_connection_superhack); EXPORT_SYMBOL(ptlrpc_abort_inflight_superhack); + +struct proc_dir_entry *proc_lustre_root; EXPORT_SYMBOL(proc_lustre_root); EXPORT_SYMBOL(class_register_type); @@ -427,28 +398,12 @@ EXPORT_SYMBOL(class_uuid2obd); EXPORT_SYMBOL(class_find_client_obd); EXPORT_SYMBOL(class_find_client_notype); EXPORT_SYMBOL(class_devices_in_group); -EXPORT_SYMBOL(__class_export_put); -EXPORT_SYMBOL(class_new_export); -EXPORT_SYMBOL(class_unlink_export); -EXPORT_SYMBOL(class_import_get); -EXPORT_SYMBOL(class_import_put); -EXPORT_SYMBOL(class_new_import); -EXPORT_SYMBOL(class_destroy_import); -EXPORT_SYMBOL(class_connect); EXPORT_SYMBOL(class_conn2export); EXPORT_SYMBOL(class_exp2obd); EXPORT_SYMBOL(class_conn2obd); EXPORT_SYMBOL(class_exp2cliimp); EXPORT_SYMBOL(class_conn2cliimp); EXPORT_SYMBOL(class_disconnect); -EXPORT_SYMBOL(class_disconnect_exports); -EXPORT_SYMBOL(class_disconnect_stale_exports); - -EXPORT_SYMBOL(oig_init); -EXPORT_SYMBOL(oig_release); -EXPORT_SYMBOL(oig_add_one); -EXPORT_SYMBOL(oig_wait); -EXPORT_SYMBOL(oig_complete_one); /* uuid.c */ EXPORT_SYMBOL(class_uuid_unparse); @@ -497,15 +452,58 @@ int obd_proc_read_pinger(char *page, char **start, off_t off, int count, ); } +static int obd_proc_read_health(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int rc = 0; //, i; + *eof = 1; + + if (portals_catastrophe) + rc += snprintf(page + rc, count - rc, "LBUG\n"); + +#if 0 + spin_lock(&obd_dev_lock); + for (i = 0; i < MAX_OBD_DEVICES; i++) { + struct obd_device *obd; + + obd = &obd_dev[i]; + if (obd->obd_type == NULL) + continue; + + atomic_inc(&obd->obd_refcount); + spin_unlock(&obd_dev_lock); + + if (obd_health_check(obd)) { + rc += snprintf(page + rc, count - rc, + "device %s reported unhealthy\n", + obd->obd_name); + } + class_decref(obd); + spin_lock(&obd_dev_lock); + } + spin_unlock(&obd_dev_lock); +#endif + + if (rc == 0) + return snprintf(page, count, "healthy\n"); + + rc += snprintf(page + rc, count - rc, "NOT HEALTHY\n"); + return rc; +} + /* Root for /proc/fs/lustre */ -struct proc_dir_entry *proc_lustre_root = NULL; struct lprocfs_vars lprocfs_base[] = { { "version", obd_proc_read_version, NULL, NULL }, { "kernel_version", obd_proc_read_kernel_version, NULL, NULL }, { "pinger", obd_proc_read_pinger, NULL, NULL }, + { "health_check", obd_proc_read_health, NULL, NULL }, { 0 } }; +#else +#define lprocfs_base NULL +#endif /* LPROCFS */ +#ifdef __KERNEL__ static void *obd_device_list_seq_start(struct seq_file *p, loff_t*pos) { if (*pos >= MAX_OBD_DEVICES) @@ -590,13 +588,13 @@ int obd_init_checks(void) CDEBUG(D_INFO, "LPU64=%s, LPD64=%s, LPX64=%s, LPSZ=%s, LPSSZ=%s\n", LPU64, LPD64, LPX64, LPSZ, LPSSZ); - CDEBUG(D_INFO, "OBD_OBJECT_EOF = "LPX64"\n", OBD_OBJECT_EOF); + CDEBUG(D_INFO, "OBD_OBJECT_EOF = "LPX64"\n", (__u64)OBD_OBJECT_EOF); u64val = OBD_OBJECT_EOF; CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val); if (u64val != OBD_OBJECT_EOF) { CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n", - u64val, sizeof(u64val)); + u64val, (int)sizeof(u64val)); ret = -EINVAL; } len = snprintf(buf, sizeof(buf), LPX64, u64val); @@ -609,12 +607,12 @@ int obd_init_checks(void) CDEBUG(D_INFO, "u64val OBD_OBJECT_EOF = "LPX64"\n", u64val); if (u64val != OBD_OBJECT_EOF) { CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n", - u64val, sizeof(u64val)); + u64val, (int)sizeof(u64val)); ret = -EOVERFLOW; } if (u64val >> 8 != OBD_OBJECT_EOF >> 8) { CERROR("__u64 "LPX64"(%d) != 0xffffffffffffffff\n", - u64val, sizeof(u64val)); + u64val, (int)sizeof(u64val)); return -EOVERFLOW; } if (do_div(div64val, 256) != (u64val & 255)) { @@ -659,7 +657,7 @@ int init_obdclass(void) #endif { struct obd_device *obd; -#ifdef LPROCFS +#ifdef __KERNEL__ struct proc_dir_entry *entry; #endif int err; @@ -696,9 +694,7 @@ int init_obdclass(void) #ifdef __KERNEL__ obd_sysctl_init(); -#endif -#ifdef LPROCFS proc_lustre_root = proc_mkdir("lustre", proc_root_fs); if (!proc_lustre_root) { printk(KERN_ERR @@ -740,12 +736,11 @@ static void cleanup_obdclass(void) obd_cleanup_caches(); obd_sysctl_clean(); -#ifdef LPROCFS + if (proc_lustre_root) { lprocfs_remove(proc_lustre_root); proc_lustre_root = NULL; } -#endif class_handle_cleanup(); class_exit_uuidlist(); @@ -761,7 +756,7 @@ static void cleanup_obdclass(void) * kernel patch */ #include <linux/lustre_version.h> #define LUSTRE_MIN_VERSION 32 -#define LUSTRE_MAX_VERSION 45 +#define LUSTRE_MAX_VERSION 46 #if (LUSTRE_KERNEL_VERSION < LUSTRE_MIN_VERSION) # error Cannot continue: Your Lustre kernel patch is older than the sources #elif (LUSTRE_KERNEL_VERSION > LUSTRE_MAX_VERSION) diff --git a/lustre/obdclass/genops.c b/lustre/obdclass/genops.c index 43767e0..165a7df 100644 --- a/lustre/obdclass/genops.c +++ b/lustre/obdclass/genops.c @@ -36,6 +36,8 @@ #else #include <liblustre.h> #include <linux/obd_class.h> +#include <linux/lustre_mds.h> +#include <linux/obd_ost.h> #include <linux/obd.h> #include <linux/lustre_mds.h> #include <linux/obd_ost.h> @@ -45,12 +47,20 @@ extern struct list_head obd_types; static spinlock_t obd_types_lock = SPIN_LOCK_UNLOCKED; + kmem_cache_t *obdo_cachep = NULL; +EXPORT_SYMBOL(obdo_cachep); kmem_cache_t *import_cachep = NULL; +#ifdef HAVE_QUOTA_SUPPORT kmem_cache_t *qunit_cachep = NULL; struct list_head qunit_hash[NR_DQHASH]; spinlock_t qunit_hash_lock = SPIN_LOCK_UNLOCKED; +EXPORT_SYMBOL(qunit_cachep); +EXPORT_SYMBOL(qunit_hash); +EXPORT_SYMBOL(qunit_hash_lock); +#endif + int (*ptlrpc_put_connection_superhack)(struct ptlrpc_connection *c); void (*ptlrpc_abort_inflight_superhack)(struct obd_import *imp); @@ -67,8 +77,7 @@ static struct obd_type *class_search_type(char *name) spin_lock(&obd_types_lock); list_for_each(tmp, &obd_types) { type = list_entry(tmp, struct obd_type, typ_chain); - if (strlen(type->typ_name) == strlen(name) && - strcmp(type->typ_name, name) == 0) { + if (strcmp(type->typ_name, name) == 0) { spin_unlock(&obd_types_lock); return type; } @@ -243,7 +252,7 @@ int class_name2dev(char *name) if (obd->obd_name && strcmp(name, obd->obd_name) == 0) { /* Make sure we finished attaching before we give out any references */ - if (obd->obd_attached) { + if (obd->obd_attached) { spin_unlock(&obd_dev_lock); return i; } @@ -365,6 +374,7 @@ struct obd_device * class_devices_in_group(struct obd_uuid *grp_uuid, int *next) static void obd_cleanup_qunit_cache(void) { +#ifdef HAVE_QUOTA_SUPPORT int i; ENTRY; @@ -372,13 +382,14 @@ static void obd_cleanup_qunit_cache(void) for (i = 0; i < NR_DQHASH; i++) LASSERT(list_empty(qunit_hash + i)); spin_unlock(&qunit_hash_lock); - + if (qunit_cachep) { LASSERTF(kmem_cache_destroy(qunit_cachep) == 0, "Cannot destroy ll_qunit_cache\n"); qunit_cachep = NULL; } EXIT; +#endif } void obd_cleanup_caches(void) @@ -400,11 +411,13 @@ void obd_cleanup_caches(void) static int obd_init_qunit_cache(void) { + +#ifdef HAVE_QUOTA_SUPPORT int i; ENTRY; - + LASSERT(qunit_cachep == NULL); - qunit_cachep = kmem_cache_create("ll_qunit_cache", + qunit_cachep = kmem_cache_create("ll_qunit_cache", sizeof(struct lustre_qunit), 0, 0, NULL, NULL); if (!qunit_cachep) @@ -414,6 +427,7 @@ static int obd_init_qunit_cache(void) for (i = 0; i < NR_DQHASH; i++) INIT_LIST_HEAD(qunit_hash + i); spin_unlock(&qunit_hash_lock); +#endif RETURN(0); } @@ -529,6 +543,7 @@ void __class_export_put(struct obd_export *exp) class_decref(obd); } } +EXPORT_SYMBOL(__class_export_put); /* Creates a new export, adds it to the hash table, and returns a * pointer to it. The refcount is 2: one for the hash reference, and @@ -552,17 +567,22 @@ struct obd_export *class_new_export(struct obd_device *obd) INIT_LIST_HEAD(&export->exp_handle.h_link); class_handle_hash(&export->exp_handle, export_handle_addref); + export->exp_last_request_time = CURRENT_SECONDS; spin_lock_init(&export->exp_lock); spin_lock(&obd->obd_dev_lock); LASSERT(!obd->obd_stopping); /* shouldn't happen, but might race */ atomic_inc(&obd->obd_refcount); list_add(&export->exp_obd_chain, &export->exp_obd->obd_exports); + list_add_tail(&export->exp_obd_chain_timed, + &export->exp_obd->obd_exports_timed); export->exp_obd->obd_num_exports++; spin_unlock(&obd->obd_dev_lock); + obd_init_export(export); return export; } +EXPORT_SYMBOL(class_new_export); void class_unlink_export(struct obd_export *exp) { @@ -570,11 +590,13 @@ void class_unlink_export(struct obd_export *exp) spin_lock(&exp->exp_obd->obd_dev_lock); list_del_init(&exp->exp_obd_chain); + list_del_init(&exp->exp_obd_chain_timed); exp->exp_obd->obd_num_exports--; spin_unlock(&exp->exp_obd->obd_dev_lock); class_export_put(exp); } +EXPORT_SYMBOL(class_unlink_export); /* Import management functions */ static void import_handle_addref(void *import) @@ -584,11 +606,14 @@ static void import_handle_addref(void *import) struct obd_import *class_import_get(struct obd_import *import) { + LASSERT(atomic_read(&import->imp_refcount) >= 0); + LASSERT(atomic_read(&import->imp_refcount) < 0x5a5a5a); atomic_inc(&import->imp_refcount); CDEBUG(D_IOCTL, "import %p refcount=%d\n", import, atomic_read(&import->imp_refcount)); return import; } +EXPORT_SYMBOL(class_import_get); void class_import_put(struct obd_import *import) { @@ -622,6 +647,7 @@ void class_import_put(struct obd_import *import) OBD_FREE(import, sizeof(*import)); EXIT; } +EXPORT_SYMBOL(class_import_put); struct obd_import *class_new_import(void) { @@ -650,6 +676,7 @@ struct obd_import *class_new_import(void) return imp; } +EXPORT_SYMBOL(class_new_import); void class_destroy_import(struct obd_import *import) { @@ -667,6 +694,7 @@ void class_destroy_import(struct obd_import *import) class_import_put(import); } +EXPORT_SYMBOL(class_destroy_import); /* A connection defines an export context in which preallocation can be managed. This releases the export pointer reference, and returns @@ -694,6 +722,7 @@ int class_connect(struct lustre_handle *conn, struct obd_device *obd, cluuid->uuid, conn->cookie); RETURN(0); } +EXPORT_SYMBOL(class_connect); /* This function removes two references from the export: one for the * hash entry and one for the export pointer passed in. The export @@ -701,6 +730,7 @@ int class_connect(struct lustre_handle *conn, struct obd_device *obd, * again. */ int class_disconnect(struct obd_export *export) { + int already_disconnected; ENTRY; if (export == NULL) { @@ -709,10 +739,15 @@ int class_disconnect(struct obd_export *export) RETURN(-EINVAL); } - /* XXX this shouldn't have to be here, but double-disconnect will crash - * otherwise, and sometimes double-disconnect happens. abort_recovery, - * for example. */ - if (list_empty(&export->exp_handle.h_link)) + spin_lock(&export->exp_lock); + already_disconnected = export->exp_disconnected; + export->exp_disconnected = 1; + spin_unlock(&export->exp_lock); + + /* class_cleanup(), abort_recovery(), and class_fail_export() + * all end up in here, and if any of them race we shouldn't + * call extra class_export_puts(). */ + if (already_disconnected) RETURN(0); CDEBUG(D_IOCTL, "disconnect: cookie "LPX64"\n", @@ -723,14 +758,14 @@ int class_disconnect(struct obd_export *export) RETURN(0); } -static void class_disconnect_export_list(struct list_head *list, int flags) +static void class_disconnect_export_list(struct list_head *list, int flags) { int rc; struct lustre_handle fake_conn; struct obd_export *fake_exp, *exp; ENTRY; - /* It's possible that an export may disconnect itself, but + /* It's possible that an export may disconnect itself, but * nothing else will be added to this list. */ while(!list_empty(list)) { exp = list_entry(list->next, struct obd_export, exp_obd_chain); @@ -790,6 +825,7 @@ void class_disconnect_exports(struct obd_device *obd) class_disconnect_export_list(&work_list, get_exp_flags_from_obd(obd)); EXIT; } +EXPORT_SYMBOL(class_disconnect_exports); /* Remove exports that have not completed recovery. */ @@ -800,7 +836,7 @@ void class_disconnect_stale_exports(struct obd_device *obd) struct obd_export *exp; int cnt = 0; ENTRY; - + INIT_LIST_HEAD(&work_list); spin_lock(&obd->obd_dev_lock); list_for_each_safe(pos, n, &obd->obd_exports) { @@ -813,11 +849,12 @@ void class_disconnect_stale_exports(struct obd_device *obd) } spin_unlock(&obd->obd_dev_lock); - CDEBUG(D_ERROR, "%s: disconnecting %d stale clients\n", + CDEBUG(D_ERROR, "%s: disconnecting %d stale clients\n", obd->obd_name, cnt); class_disconnect_export_list(&work_list, get_exp_flags_from_obd(obd)); EXIT; } +EXPORT_SYMBOL(class_disconnect_stale_exports); int oig_init(struct obd_io_group **oig_out) { @@ -838,16 +875,19 @@ int oig_init(struct obd_io_group **oig_out) *oig_out = oig; RETURN(0); }; +EXPORT_SYMBOL(oig_init); static inline void oig_grab(struct obd_io_group *oig) { atomic_inc(&oig->oig_refcount); } + void oig_release(struct obd_io_group *oig) { if (atomic_dec_and_test(&oig->oig_refcount)) OBD_FREE(oig, sizeof(*oig)); } +EXPORT_SYMBOL(oig_release); void oig_add_one(struct obd_io_group *oig, struct oig_callback_context *occ) @@ -861,6 +901,7 @@ void oig_add_one(struct obd_io_group *oig, spin_unlock_irqrestore(&oig->oig_lock, flags); oig_grab(oig); } +EXPORT_SYMBOL(oig_add_one); void oig_complete_one(struct obd_io_group *oig, struct oig_callback_context *occ, int rc) @@ -890,6 +931,7 @@ void oig_complete_one(struct obd_io_group *oig, wake_up(wake); oig_release(oig); } +EXPORT_SYMBOL(oig_complete_one); static int oig_done(struct obd_io_group *oig) { @@ -950,3 +992,265 @@ int oig_wait(struct obd_io_group *oig) CDEBUG(D_CACHE, "done waiting on oig %p rc %d\n", oig, oig->oig_rc); return oig->oig_rc; } +EXPORT_SYMBOL(oig_wait); + +void class_fail_export(struct obd_export *exp) +{ + int rc, already_failed; + unsigned long flags; + + spin_lock_irqsave(&exp->exp_lock, flags); + already_failed = exp->exp_failed; + exp->exp_failed = 1; + spin_unlock_irqrestore(&exp->exp_lock, flags); + + if (already_failed) { + CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n", + exp, exp->exp_client_uuid.uuid); + return; + } + + CDEBUG(D_HA, "disconnecting export %p/%s\n", + exp, exp->exp_client_uuid.uuid); + + if (obd_dump_on_timeout) + portals_debug_dumplog(); + + /* Most callers into obd_disconnect are removing their own reference + * (request, for example) in addition to the one from the hash table. + * We don't have such a reference here, so make one. */ + class_export_get(exp); + rc = obd_disconnect(exp); + if (rc) + CERROR("disconnecting export %p failed: %d\n", exp, rc); + else + CDEBUG(D_HA, "disconnected export %p/%s\n", + exp, exp->exp_client_uuid.uuid); +} +EXPORT_SYMBOL(class_fail_export); + +/* Ping evictor thread */ +#define D_PET D_HA + +#ifdef __KERNEL__ +#define PET_READY 1 +#define PET_TERMINATE 2 + +static int pet_refcount = 0; +static int pet_state; +static wait_queue_head_t pet_waitq; +static struct obd_export *pet_exp = NULL; +static spinlock_t pet_lock = SPIN_LOCK_UNLOCKED; + +static int ping_evictor_wake(struct obd_export *exp) +{ + spin_lock(&pet_lock); + if (pet_exp) { + /* eventually the new obd will call here again. */ + spin_unlock(&pet_lock); + return 1; + } + pet_exp = exp; + spin_unlock(&pet_lock); + + /* We have to make sure the obd isn't destroyed between now and when + the ping evictor runs. We'll take a reference here, and drop it + when we finish in the evictor. We don't really care about this + export in particular; we just need one to keep the obd. */ + class_export_get(pet_exp); + wake_up(&pet_waitq); + return 0; +} + +static int ping_evictor_main(void *arg) +{ + struct list_head *pos, *n; + struct obd_device *obd; + struct obd_export *exp; + struct l_wait_info lwi = { 0 }; + time_t expire_time; + unsigned long flags; + ENTRY; + + lock_kernel(); + kportal_daemonize("ping_evictor"); + SIGNAL_MASK_LOCK(current, flags); + sigfillset(¤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)"; +} diff --git a/lustre/obdclass/llog.c b/lustre/obdclass/llog.c index f39d614..09a295b 100644 --- a/lustre/obdclass/llog.c +++ b/lustre/obdclass/llog.c @@ -93,7 +93,7 @@ int llog_cancel_rec(struct llog_handle *loghandle, int index) } if (!ext2_clear_bit(index, llh->llh_bitmap)) { - CERROR("catalog index %u already clear?\n", index); + CDEBUG(D_HA, "catalog index %u already clear?\n", index); RETURN(-EINVAL); } diff --git a/lustre/obdclass/llog_ioctl.c b/lustre/obdclass/llog_ioctl.c index be9c4fd..c4ec77d 100644 --- a/lustre/obdclass/llog_ioctl.c +++ b/lustre/obdclass/llog_ioctl.c @@ -112,6 +112,7 @@ static int llog_check_cb(struct llog_handle *handle, struct llog_rec_hdr *rec, case MDS_UNLINK_REC: case MDS_SETATTR_REC: case OBD_CFG_REC: + case PTL_CFG_REC: /* obsolete */ case LLOG_HDR_MAGIC: { l = snprintf(out, remains, "[index]: %05d [type]: " "%02x [len]: %04d ok\n", diff --git a/lustre/obdclass/llog_lvfs.c b/lustre/obdclass/llog_lvfs.c index 6c45e8d..d9db59b 100644 --- a/lustre/obdclass/llog_lvfs.c +++ b/lustre/obdclass/llog_lvfs.c @@ -390,7 +390,8 @@ static int llog_lvfs_next_block(struct llog_handle *loghandle, int *cur_idx, } rec = buf; - tail = (struct llog_rec_tail *)((char *)buf + rc - sizeof(struct llog_rec_tail)); + tail = (struct llog_rec_tail *)((char *)buf + rc - + sizeof(struct llog_rec_tail)); if (LLOG_REC_HDR_NEEDS_SWABBING(rec)) { lustre_swab_llog_rec(rec, tail); @@ -485,7 +486,7 @@ static int llog_lvfs_create(struct llog_ctxt *ctxt, struct llog_handle **res, GOTO(cleanup, rc); } - handle->lgh_file = l_dentry_open(&obd->obd_ctxt, dchild, + handle->lgh_file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild, O_RDWR | O_LARGEFILE); if (IS_ERR(handle->lgh_file)) { rc = PTR_ERR(handle->lgh_file); @@ -524,7 +525,7 @@ static int llog_lvfs_create(struct llog_ctxt *ctxt, struct llog_handle **res, if (IS_ERR(dchild)) GOTO(cleanup, rc = PTR_ERR(dchild)); cleanup_phase = 2; - handle->lgh_file = l_dentry_open(&obd->obd_ctxt, dchild, + handle->lgh_file = l_dentry_open(&obd->obd_lvfs_ctxt, dchild, open_flags); if (IS_ERR(handle->lgh_file)) GOTO(cleanup, rc = PTR_ERR(handle->lgh_file)); @@ -571,9 +572,9 @@ static int llog_lvfs_destroy(struct llog_handle *handle) if (!strcmp(fdentry->d_parent->d_name.name, "LOGS")) { struct obd_device *obd = handle->lgh_ctxt->loc_exp->exp_obd; struct inode *inode = fdentry->d_parent->d_inode; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); dget(fdentry); rc = llog_lvfs_close(handle); @@ -584,7 +585,7 @@ static int llog_lvfs_destroy(struct llog_handle *handle) } dput(fdentry); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -611,7 +612,7 @@ static int llog_lvfs_destroy(struct llog_handle *handle) int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd, char *name, int count, struct llog_catid *idarray) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct l_file *file; int rc; int size = sizeof(*idarray) * count; @@ -619,7 +620,7 @@ int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd, LASSERT(count); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); file = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0700); if (!file || IS_ERR(file)) { rc = PTR_ERR(file); @@ -642,7 +643,7 @@ int llog_get_cat_list(struct obd_device *obd, struct obd_device *disk_obd, } out: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (file && !IS_ERR(file)) rc = filp_close(file, 0); RETURN(rc); @@ -653,7 +654,7 @@ EXPORT_SYMBOL(llog_get_cat_list); int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd, char *name, int count, struct llog_catid *idarray) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct l_file *file; int rc; int size = sizeof(*idarray) * count; @@ -661,7 +662,7 @@ int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd, LASSERT(count); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); file = filp_open(name, O_RDWR | O_CREAT | O_LARGEFILE, 0700); if (!file || IS_ERR(file)) { rc = PTR_ERR(file); @@ -684,7 +685,7 @@ int llog_put_cat_list(struct obd_device *obd, struct obd_device *disk_obd, } out: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (file && !IS_ERR(file)) rc = filp_close(file, 0); RETURN(rc); diff --git a/lustre/obdclass/llog_obd.c b/lustre/obdclass/llog_obd.c index 0796c50..ca5144c 100644 --- a/lustre/obdclass/llog_obd.c +++ b/lustre/obdclass/llog_obd.c @@ -68,7 +68,6 @@ int llog_cleanup(struct llog_ctxt *ctxt) rc = CTXTP(ctxt, cleanup)(ctxt); ctxt->loc_obd->obd_llog_ctxt[ctxt->loc_idx] = NULL; - ctxt->loc_exp = NULL; OBD_FREE(ctxt, sizeof(*ctxt)); RETURN(rc); @@ -182,7 +181,7 @@ int llog_obd_origin_setup(struct obd_device *obd, int index, { struct llog_ctxt *ctxt; struct llog_handle *handle; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc; ENTRY; @@ -206,9 +205,9 @@ int llog_obd_origin_setup(struct obd_device *obd, int index, GOTO(out, rc); ctxt->loc_handle = handle; - push_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); rc = llog_init_handle(handle, LLOG_F_IS_CAT, NULL); - pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); if (rc) GOTO(out, rc); diff --git a/lustre/obdclass/llog_swab.c b/lustre/obdclass/llog_swab.c index f92c2ef..3ae0a36 100644 --- a/lustre/obdclass/llog_swab.c +++ b/lustre/obdclass/llog_swab.c @@ -25,6 +25,10 @@ #define DEBUG_SUBSYSTEM S_LOG +#ifndef __KERNEL__ +#include <liblustre.h> +#endif + #include <linux/lustre_log.h> static void print_llogd_body(struct llogd_body *d) @@ -119,6 +123,7 @@ void lustre_swab_llog_rec(struct llog_rec_hdr *rec, struct llog_rec_tail *tail) } case OBD_CFG_REC: + case PTL_CFG_REC: /* obsolete */ /* these are swabbed as they are consumed */ break; @@ -148,6 +153,7 @@ void lustre_swab_llog_rec(struct llog_rec_hdr *rec, struct llog_rec_tail *tail) break; } + case LLOG_PAD_MAGIC: /* ignore old pad records of type 0 */ case 0: break; diff --git a/lustre/obdclass/llog_test.c b/lustre/obdclass/llog_test.c index e694153..7007632 100644 --- a/lustre/obdclass/llog_test.c +++ b/lustre/obdclass/llog_test.c @@ -529,14 +529,14 @@ static int llog_test_7(struct obd_device *obd) static int llog_run_tests(struct obd_device *obd) { struct llog_handle *llh; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_TEST_ORIG_CTXT); int rc, err, cleanup_phase = 0; char name[10]; ENTRY; sprintf(name, "%x", llog_test_rand); - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); rc = llog_test_1(obd, name); if (rc) @@ -576,7 +576,7 @@ static int llog_run_tests(struct obd_device *obd) if (!rc) rc = err; case 0: - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); } return rc; @@ -662,9 +662,11 @@ static struct obd_ops llog_obd_ops = { .o_llog_finish = llog_test_llog_finish, }; +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; LPROCFS_INIT_VARS(llog_test, lprocfs_module_vars, lprocfs_obd_vars) +#endif static int __init llog_test_init(void) { diff --git a/lustre/obdclass/lprocfs_status.c b/lustre/obdclass/lprocfs_status.c index 1b663e9..8a0db22 100644 --- a/lustre/obdclass/lprocfs_status.c +++ b/lustre/obdclass/lprocfs_status.c @@ -43,7 +43,7 @@ #include <linux/lprocfs_status.h> #include <linux/lustre_fsfilt.h> -#if defined(LPROCFS) && defined(__KERNEL__) +#if defined(LPROCFS) struct proc_dir_entry *lprocfs_srch(struct proc_dir_entry *head, const char *name) @@ -204,11 +204,11 @@ int lprocfs_rd_atomic(char *page, char **start, off_t off, int lprocfs_rd_uuid(char *page, char **start, off_t off, int count, int *eof, void *data) { - struct obd_device *dev = (struct obd_device*)data; + struct obd_device *obd = (struct obd_device*)data; - LASSERT(dev != NULL); + LASSERT(obd != NULL); *eof = 1; - return snprintf(page, count, "%s\n", dev->obd_uuid.uuid); + return snprintf(page, count, "%s\n", obd->obd_uuid.uuid); } int lprocfs_rd_name(char *page, char **start, off_t off, int count, @@ -335,8 +335,9 @@ int lprocfs_rd_server_uuid(char *page, char **start, off_t off, int count, imp = obd->u.cli.cl_import; imp_state_name = ptlrpc_import_state_name(imp->imp_state); *eof = 1; - return snprintf(page, count, "%s\t%s\n", - imp->imp_target_uuid.uuid, imp_state_name); + return snprintf(page, count, "%s\t%s%s\n", + imp->imp_target_uuid.uuid, imp_state_name, + imp->imp_deactive ? "\tDEACTIVATED" : ""); } int lprocfs_rd_conn_uuid(char *page, char **start, off_t off, int count, @@ -873,8 +874,6 @@ int lprocfs_obd_rd_recovery_status(char *page, char **start, off_t off, } EXPORT_SYMBOL(lprocfs_obd_rd_recovery_status); -#endif /* LPROCFS*/ - EXPORT_SYMBOL(lprocfs_register); EXPORT_SYMBOL(lprocfs_srch); EXPORT_SYMBOL(lprocfs_remove); @@ -906,3 +905,4 @@ EXPORT_SYMBOL(lprocfs_rd_filesfree); EXPORT_SYMBOL(lprocfs_write_helper); EXPORT_SYMBOL(lprocfs_write_u64_helper); +#endif /* LPROCFS*/ diff --git a/lustre/obdclass/obd_config.c b/lustre/obdclass/obd_config.c index e80bb58..f4835c6 100644 --- a/lustre/obdclass/obd_config.c +++ b/lustre/obdclass/obd_config.c @@ -81,7 +81,7 @@ int class_attach(struct lustre_cfg *lcfg) len = strlen(name) + 1; OBD_ALLOC(namecopy, len); - if (!namecopy) + if (!namecopy) GOTO(out, rc = -ENOMEM); memcpy(namecopy, name, len); cleanup_phase = 2; /* free obd_name */ @@ -95,6 +95,7 @@ int class_attach(struct lustre_cfg *lcfg) cleanup_phase = 3; /* class_release_dev */ INIT_LIST_HEAD(&obd->obd_exports); + INIT_LIST_HEAD(&obd->obd_exports_timed); obd->obd_num_exports = 0; spin_lock_init(&obd->obd_dev_lock); spin_lock_init(&obd->obd_osfs_lock); @@ -126,7 +127,7 @@ int class_attach(struct lustre_cfg *lcfg) GOTO(out, rc = -EINVAL); } - /* The attach is our first obd reference */ + /* Detach drops this */ atomic_set(&obd->obd_refcount, 1); obd->obd_attached = 1; @@ -159,7 +160,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg) CERROR("Device %d not attached\n", obd->obd_minor); RETURN(-ENODEV); } - + if (obd->obd_set_up) { CERROR("Device %d already setup (type %s)\n", obd->obd_minor, obd->obd_type->typ_name); @@ -176,7 +177,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg) } /* just leave this on forever. I can't use obd_set_up here because other fns check that status, and we're not actually set up yet. */ - obd->obd_starting = 1; + obd->obd_starting = 1; spin_unlock(&obd->obd_dev_lock); exp = class_new_export(obd); @@ -185,6 +186,7 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg) memcpy(&exp->exp_client_uuid, &obd->obd_uuid, sizeof(exp->exp_client_uuid)); obd->obd_self_export = exp; + list_del_init(&exp->exp_obd_chain_timed); class_export_put(exp); err = obd_setup(obd, sizeof(*lcfg), lcfg); @@ -193,9 +195,14 @@ int class_setup(struct obd_device *obd, struct lustre_cfg *lcfg) obd->obd_type->typ_refcnt++; obd->obd_set_up = 1; + spin_lock(&obd->obd_dev_lock); + /* cleanup drops this */ + atomic_inc(&obd->obd_refcount); + spin_unlock(&obd->obd_dev_lock); + CDEBUG(D_IOCTL, "finished setup of obd %s (uuid %s)\n", obd->obd_name, obd->obd_uuid.uuid); - + RETURN(0); err_exp: @@ -208,28 +215,33 @@ err_exp: static int __class_detach(struct obd_device *obd) { int err = 0; + ENTRY; + + CDEBUG(D_CONFIG, "destroying obd %d (%s)\n", + obd->obd_minor, obd->obd_name); - if (OBP(obd, detach)) + if (OBP(obd, detach)) err = OBP(obd,detach)(obd); - + if (obd->obd_name) { OBD_FREE(obd->obd_name, strlen(obd->obd_name)+1); obd->obd_name = NULL; } else { CERROR("device %d: no name at detach\n", obd->obd_minor); } - + LASSERT(OBT(obd)); /* Attach took type refcount */ obd->obd_type->typ_refcnt--; class_put_type(obd->obd_type); class_release_dev(obd); - return (err); + RETURN(err); } int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg) { ENTRY; + if (obd->obd_set_up) { CERROR("OBD device %d still set up\n", obd->obd_minor); RETURN(-EBUSY); @@ -243,7 +255,7 @@ int class_detach(struct obd_device *obd, struct lustre_cfg *lcfg) } obd->obd_attached = 0; spin_unlock(&obd->obd_dev_lock); - + CDEBUG(D_IOCTL, "detach on obd %s (uuid %s)\n", obd->obd_name, obd->obd_uuid.uuid); @@ -267,8 +279,9 @@ static void dump_exports(struct obd_device *obd) nreplies++; } - CDEBUG(D_IOCTL, "%s: %p %s %d %d %d: %p %s\n", + CDEBUG(D_IOCTL, "%s: %p %s %s %d %d %d: %p %s\n", obd->obd_name, exp, exp->exp_client_uuid.uuid, + obd_export_nid2str(exp), atomic_read(&exp->exp_refcount), exp->exp_failed, nreplies, first_reply, nreplies > 3 ? "..." : ""); @@ -279,8 +292,8 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg) { int err = 0; char *flag; - ENTRY; + OBD_RACE(OBD_FAIL_LDLM_RECOV_CLIENTS); if (!obd->obd_set_up) { @@ -309,6 +322,7 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg) obd->obd_name); obd->obd_fail = 1; obd->obd_no_transno = 1; + obd->obd_no_recov = 1; /* Set the obd readonly if we can */ if (OBP(obd, iocontrol)) obd_iocontrol(OBD_IOC_SET_READONLY, @@ -320,39 +334,37 @@ int class_cleanup(struct obd_device *obd, struct lustre_cfg *lcfg) *flag); } } - - /* The two references that should be remaining are the - * obd_self_export and the attach reference. */ - if (atomic_read(&obd->obd_refcount) > 2) { + + /* The three references that should be remaining are the + * obd_self_export and the attach and setup references. */ + if (atomic_read(&obd->obd_refcount) > 3) { if (!(obd->obd_fail || obd->obd_force)) { CERROR("OBD %s is still busy with %d references\n" "You should stop active file system users," - " or use the --force option to cleanup.\n", + " or use the --force option to cleanup.\n", obd->obd_name, atomic_read(&obd->obd_refcount)); dump_exports(obd); GOTO(out, err = -EBUSY); } CDEBUG(D_IOCTL, "%s: forcing exports to disconnect: %d\n", - obd->obd_name, atomic_read(&obd->obd_refcount)); + obd->obd_name, atomic_read(&obd->obd_refcount) - 1); dump_exports(obd); class_disconnect_exports(obd); } LASSERT(obd->obd_self_export); - if (obd->obd_self_export) { - /* mds_precleanup will clean up the lov (and osc's)*/ - err = obd_precleanup(obd); - if (err) - GOTO(out, err); - obd->obd_self_export->exp_flags |= - (obd->obd_fail ? OBD_OPT_FAILOVER : 0) | - (obd->obd_force ? OBD_OPT_FORCE : 0); - class_unlink_export(obd->obd_self_export); - obd->obd_self_export = NULL; - } + /* Precleanup stage 1, we must make sure all exports (other than the + self-export) get destroyed. */ + err = obd_precleanup(obd, 1); + if (err) + CERROR("Precleanup %s returned %d\n", + obd->obd_name, err); + + class_decref(obd); obd->obd_set_up = 0; obd->obd_type->typ_refcnt--; + RETURN(0); out: /* Allow a failed cleanup to try again. */ @@ -362,16 +374,45 @@ out: void class_decref(struct obd_device *obd) { - if (atomic_dec_and_test(&obd->obd_refcount)) { - int err; - CDEBUG(D_IOCTL, "finishing cleanup of obd %s (%s)\n", + int err; + int refs; + + spin_lock(&obd->obd_dev_lock); + atomic_dec(&obd->obd_refcount); + refs = atomic_read(&obd->obd_refcount); + spin_unlock(&obd->obd_dev_lock); + + CDEBUG(D_INFO, "Decref %s now %d\n", obd->obd_name, refs); + + if ((refs == 1) && obd->obd_stopping) { + /* All exports (other than the self-export) have been + destroyed; there should be no more in-progress ops + by this point.*/ + /* if we're not stopping, we didn't finish setup */ + /* Precleanup stage 2, do other type-specific + cleanup requiring the self-export. */ + err = obd_precleanup(obd, 2); + if (err) + CERROR("Precleanup %s returned %d\n", + obd->obd_name, err); + obd->obd_self_export->exp_flags |= + (obd->obd_fail ? OBD_OPT_FAILOVER : 0) | + (obd->obd_force ? OBD_OPT_FORCE : 0); + /* note that we'll recurse into class_decref again */ + class_unlink_export(obd->obd_self_export); + return; + } + + if (refs == 0) { + CDEBUG(D_CONFIG, "finishing cleanup of obd %s (%s)\n", obd->obd_name, obd->obd_uuid.uuid); LASSERT(!obd->obd_attached); if (obd->obd_stopping) { - /* If we're not stopping, we never set up */ + /* If we're not stopping, we were never set up */ err = obd_cleanup(obd); if (err) - CERROR("Cleanup returned %d\n", err); + CERROR("Cleanup %s returned %d\n", + obd->obd_name, err); } err = __class_detach(obd); if (err) @@ -520,9 +561,9 @@ int class_process_config(struct lustre_cfg *lcfg) GOTO(out, err); } case LCFG_ADD_UUID: { - CDEBUG(D_IOCTL, "adding mapping: uuid %s -> nid %s("LPX64")\n", - lustre_cfg_string(lcfg, 1), - libcfs_nid2str(lcfg->lcfg_nid), lcfg->lcfg_nid); + CDEBUG(D_IOCTL, "adding mapping from uuid %s to nid "LPX64 + " (%s)\n", lustre_cfg_string(lcfg, 1), + lcfg->lcfg_nid, libcfs_nid2str(lcfg->lcfg_nid)); err = class_add_uuid(lustre_cfg_string(lcfg, 1), lcfg->lcfg_nid); GOTO(out, err); @@ -561,7 +602,11 @@ int class_process_config(struct lustre_cfg *lcfg) case LCFG_SET_TIMEOUT: { CDEBUG(D_IOCTL, "changing lustre timeout from %d to %d\n", obd_timeout, lcfg->lcfg_num); - obd_timeout = lcfg->lcfg_num; + obd_timeout = max(lcfg->lcfg_num, 1U); + if (ldlm_timeout >= obd_timeout) + ldlm_timeout = max(obd_timeout / 3, 1U); + else if (ldlm_timeout < 10 && obd_timeout >= ldlm_timeout * 4) + ldlm_timeout = min(obd_timeout / 3, 30U); GOTO(out, err = 0); } case LCFG_SET_UPCALL: { @@ -569,7 +614,7 @@ int class_process_config(struct lustre_cfg *lcfg) lustre_cfg_string(lcfg, 1)); if (LUSTRE_CFG_BUFLEN(lcfg, 1) > sizeof obd_lustre_upcall) GOTO(out, err = -EINVAL); - strncpy(obd_lustre_upcall, lustre_cfg_string(lcfg, 1), + strncpy(obd_lustre_upcall, lustre_cfg_string(lcfg, 1), sizeof (obd_lustre_upcall)); GOTO(out, err = 0); } @@ -665,7 +710,23 @@ static int class_config_llog_handler(struct llog_handle * handle, lcfg_new->lcfg_num = lcfg->lcfg_num; lcfg_new->lcfg_flags = lcfg->lcfg_flags; - lcfg_new->lcfg_nid = lcfg->lcfg_nid; + + /* XXX Hack to try to remain binary compatible with + * pre-newconfig logs */ + if (lcfg->lcfg_nal != 0 && /* pre-newconfig log? */ + (lcfg->lcfg_nid >> 32) == 0) { + __u32 addr = (__u32)(lcfg->lcfg_nid & 0xffffffff); + + lcfg_new->lcfg_nid = + PTL_MKNID(PTL_MKNET(lcfg->lcfg_nal, 0), addr); + CWARN("Converted pre-newconfig NAL %d NID %x to %s\n", + lcfg->lcfg_nal, addr, + libcfs_nid2str(lcfg_new->lcfg_nid)); + } else { + lcfg_new->lcfg_nid = lcfg->lcfg_nid; + } + + lcfg_new->lcfg_nal = 0; /* illegal value for obsolete field */ rc = class_process_config(lcfg_new); lustre_cfg_free(lcfg_new); @@ -674,6 +735,10 @@ static int class_config_llog_handler(struct llog_handle * handle, OBD_FREE(inst_name, inst_len); break; } + case PTL_CFG_REC: { + CWARN("Ignoring obsolete portals config\n"); + break; + } default: CERROR("Unknown llog record type %#x encountered\n", rec->lrh_type); @@ -734,12 +799,16 @@ int class_config_dump_handler(struct llog_handle * handle, if (lcfg->lcfg_nid) CDEBUG(D_INFO, " nid: "LPX64"\n", lcfg->lcfg_nid); + if (lcfg->lcfg_nal) + CDEBUG(D_INFO, " nal: %x (obsolete)\n", lcfg->lcfg_nal); if (lcfg->lcfg_num) - CDEBUG(D_INFO, " nal: %x\n", lcfg->lcfg_num); + CDEBUG(D_INFO, " num: %x\n", lcfg->lcfg_num); for (i = 1; i < lcfg->lcfg_bufcount; i++) if (LUSTRE_CFG_BUFLEN(lcfg, i) > 0) CDEBUG(D_INFO, " inlbuf%d: %s\n", i, lustre_cfg_string(lcfg, i)); + } else if (rec->lrh_type == PTL_CFG_REC) { + CDEBUG(D_INFO, "Obsolete pcfg command\n"); } else { CERROR("unhandled lrh_type: %#x\n", rec->lrh_type); rc = -EINVAL; diff --git a/lustre/obdclass/simple.c b/lustre/obdclass/simple.c deleted file mode 100644 index 581532b..0000000 --- a/lustre/obdclass/simple.c +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- - * vim:expandtab:shiftwidth=8:tabstop=8: - * - * Copyright (C) 2002, 2003 Cluster File Systems, Inc. - * Author: Peter Braam <braam@clusterfs.com> - * Aurhot: Andreas Dilger <adilger@clusterfs.com> - * - * This file is part of Lustre, http://www.lustre.org. - * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef EXPORT_SYMTAB -# define EXPORT_SYMTAB -#endif - -#include <linux/version.h> -#include <linux/fs.h> -#include <asm/unistd.h> - -#define DEBUG_SUBSYSTEM S_FILTER - -#include <linux/obd.h> -#include <linux/lustre_lib.h> -#include <linux/lustre_compat25.h> - -/* Debugging check only needed during development */ -#ifdef OBD_CTXT_DEBUG -# define ASSERT_CTXT_MAGIC(magic) LASSERT((magic) == OBD_RUN_CTXT_MAGIC) -# define ASSERT_NOT_KERNEL_CTXT(msg) LASSERT(!segment_eq(get_fs(), get_ds())) -# define ASSERT_KERNEL_CTXT(msg) LASSERT(segment_eq(get_fs(), get_ds())) -#else -# define ASSERT_CTXT_MAGIC(magic) do {} while(0) -# define ASSERT_NOT_KERNEL_CTXT(msg) do {} while(0) -# define ASSERT_KERNEL_CTXT(msg) do {} while(0) -#endif - -/* push / pop to root of obd store */ -void push_ctxt(struct obd_run_ctxt *save, struct obd_run_ctxt *new_ctx, - struct obd_ucred *uc) -{ - //ASSERT_NOT_KERNEL_CTXT("already in kernel context!\n"); - ASSERT_CTXT_MAGIC(new_ctx->magic); - OBD_SET_CTXT_MAGIC(save); - - /* - CDEBUG(D_INFO, - "= push %p->%p = cur fs %p pwd %p:d%d:i%d (%.*s), pwdmnt %p:%d\n", - save, current, current->fs, current->fs->pwd, - atomic_read(¤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); - diff --git a/lustre/obdclass/sysctl.c b/lustre/obdclass/sysctl.c index e4704f2..1bf8a27 100644 --- a/lustre/obdclass/sysctl.c +++ b/lustre/obdclass/sysctl.c @@ -58,31 +58,47 @@ enum { }; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) -int proc_fail_loc(ctl_table *table, int write, struct file *filp, - void *buffer, size_t *lenp) +#define ll_proc_dointvec(table,write,filp,buffer,lenp,ppos) \ + proc_dointvec(table,write,filp,buffer,lenp) +#define LL_PROC_PROTO(name) \ + name(ctl_table *table, int write, struct file *filp, \ + void *buffer, size_t *lenp) #else -int proc_fail_loc(ctl_table *table, int write, struct file *filp, +#define ll_proc_dointvec(table,write,filp,buffer,lenp,ppos) \ + proc_dointvec(table,write,filp,buffer,lenp,ppos); +#define LL_PROC_PROTO(name) \ + name(ctl_table *table, int write, struct file *filp, \ void *buffer, size_t *lenp, loff_t *ppos) #endif + +int LL_PROC_PROTO(proc_fail_loc) { int rc; int old_fail_loc = obd_fail_loc; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) - rc = proc_dointvec(table,write,filp,buffer,lenp); -#else - rc = proc_dointvec(table,write,filp,buffer,lenp,ppos); -#endif + rc = ll_proc_dointvec(table, write, filp, buffer, lenp, ppos); if (old_fail_loc != obd_fail_loc) wake_up(&obd_race_waitq); return rc; } +int LL_PROC_PROTO(proc_set_timeout) +{ + int rc; + + rc = ll_proc_dointvec(table, write, filp, buffer, lenp, ppos); + if (ldlm_timeout >= obd_timeout) + ldlm_timeout = max(obd_timeout / 3, 1U); + else if (ldlm_timeout < 10 && obd_timeout >= ldlm_timeout * 4) + ldlm_timeout = min(obd_timeout / 3, 30U); + return rc; +} + static ctl_table obd_table[] = { {OBD_FAIL_LOC, "fail_loc", &obd_fail_loc, sizeof(int), 0644, NULL, - &proc_dointvec}, - {OBD_TIMEOUT, "timeout", &obd_timeout, sizeof(int), 0644, NULL, &proc_fail_loc}, + {OBD_TIMEOUT, "timeout", &obd_timeout, sizeof(int), 0644, NULL, + &proc_set_timeout}, {OBD_DUMP_ON_TIMEOUT, "dump_on_timeout", &obd_dump_on_timeout, sizeof(int), 0644, NULL, &proc_dointvec}, /* XXX need to lock so we avoid update races with recovery upcall! */ @@ -93,7 +109,7 @@ static ctl_table obd_table[] = { {OBD_SYNCFILTER, "filter_sync_on_commit", &obd_sync_filter, sizeof(int), 0644, NULL, &proc_dointvec}, {OBD_LDLM_TIMEOUT, "ldlm_timeout", &ldlm_timeout, sizeof(int), 0644, - NULL, &proc_dointvec}, + NULL, &proc_set_timeout}, { 0 } }; diff --git a/lustre/obdclass/uuid.c b/lustre/obdclass/uuid.c index 2226f90..dd66ae5 100644 --- a/lustre/obdclass/uuid.c +++ b/lustre/obdclass/uuid.c @@ -141,26 +141,3 @@ void class_uuid_unparse(class_uuid_t uu, struct obd_uuid *out) uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5]); } - -struct obd_device *client_tgtuuid2obd(struct obd_uuid *tgtuuid) -{ - int i; - - for (i = 0; i < MAX_OBD_DEVICES; i++) { - struct obd_device *obd = &obd_dev[i]; - if (obd->obd_type == NULL) - continue; - if ((strncmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME, - sizeof LUSTRE_OSC_NAME) == 0) || - (strncmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME, - sizeof LUSTRE_MDC_NAME) == 0)) { - struct client_obd *cli = &obd->u.cli; - struct obd_import *imp = cli->cl_import; - if (strncmp(tgtuuid->uuid, imp->imp_target_uuid.uuid, - sizeof(imp->imp_target_uuid)) == 0) - return obd; - } - } - - return NULL; -} diff --git a/lustre/obdecho/autoMakefile.am b/lustre/obdecho/autoMakefile.am index 24d8044..834b082 100644 --- a/lustre/obdecho/autoMakefile.am +++ b/lustre/obdecho/autoMakefile.am @@ -14,5 +14,5 @@ if MODULES modulefs_DATA = obdecho$(KMODEXT) endif # MODULES -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ DIST_SOURCES = $(obdecho-objs:%.o=%.c) diff --git a/lustre/obdecho/echo.c b/lustre/obdecho/echo.c index 9c85e8d..ccc85a0 100644 --- a/lustre/obdecho/echo.c +++ b/lustre/obdecho/echo.c @@ -94,7 +94,7 @@ static int echo_disconnect(struct obd_export *exp) static int echo_destroy_export(struct obd_export *exp) { ENTRY; - + target_destroy_export(exp); RETURN(0); @@ -117,7 +117,7 @@ int echo_create(struct obd_export *exp, struct obdo *oa, struct obd_device *obd = class_exp2obd(exp); if (!obd) { - CERROR("invalid client cookie "LPX64"\n", + CERROR("invalid client cookie "LPX64"\n", exp->exp_handle.h_cookie); return -EINVAL; } @@ -169,7 +169,7 @@ static int echo_getattr(struct obd_export *exp, struct obdo *oa, obd_id id = oa->o_id; if (!obd) { - CERROR("invalid client cookie "LPX64"\n", + CERROR("invalid client cookie "LPX64"\n", exp->exp_handle.h_cookie); RETURN(-EINVAL); } @@ -191,7 +191,7 @@ static int echo_setattr(struct obd_export *exp, struct obdo *oa, struct obd_device *obd = class_exp2obd(exp); if (!obd) { - CERROR("invalid client cookie "LPX64"\n", + CERROR("invalid client cookie "LPX64"\n", exp->exp_handle.h_cookie); RETURN(-EINVAL); } @@ -231,7 +231,7 @@ echo_page_debug_setup(struct page *page, int rw, obd_id id, block_debug_setup(addr, OBD_ECHO_BLOCK_SIZE, 0xecc0ecc0ecc0ecc0ULL, 0xecc0ecc0ecc0ecc0ULL); - + addr += OBD_ECHO_BLOCK_SIZE; offset += OBD_ECHO_BLOCK_SIZE; len -= OBD_ECHO_BLOCK_SIZE; @@ -258,7 +258,7 @@ echo_page_debug_check(struct page *page, obd_id id, if (rc2 != 0 && rc == 0) rc = rc2; - + addr += OBD_ECHO_BLOCK_SIZE; offset += OBD_ECHO_BLOCK_SIZE; len -= OBD_ECHO_BLOCK_SIZE; @@ -429,7 +429,7 @@ int echo_commitrw(int cmd, struct obd_export *export, struct obdo *oa, r->page, addr, r->offset); if (verify) { - vrc = echo_page_debug_check(page, obj->ioo_id, + vrc = echo_page_debug_check(page, obj->ioo_id, r->offset, r->len); /* check all the pages always */ if (vrc != 0 && rc == 0) @@ -513,7 +513,7 @@ static int echo_cleanup(struct obd_device *obd) * happened before calling ldlm_namespace_free() */ set_current_state (TASK_UNINTERRUPTIBLE); schedule_timeout (HZ); - + ldlm_namespace_free(obd->obd_namespace, obd->obd_force); leaked = atomic_read(&obd->u.echo.eo_prep); diff --git a/lustre/obdecho/lproc_echo.c b/lustre/obdecho/lproc_echo.c index c25d156..cc3207d 100644 --- a/lustre/obdecho/lproc_echo.c +++ b/lustre/obdecho/lproc_echo.c @@ -24,11 +24,7 @@ #include <linux/lprocfs_status.h> #include <linux/obd_class.h> -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -#else - +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { 0 } @@ -39,5 +35,5 @@ static struct lprocfs_vars lprocfs_module_vars[] = { { 0 } }; -#endif /* LPROCFS */ LPROCFS_INIT_VARS(echo, lprocfs_module_vars, lprocfs_obd_vars) +#endif /* LPROCFS */ diff --git a/lustre/obdfilter/Makefile.in b/lustre/obdfilter/Makefile.in index eef89e8..0f25c77 100644 --- a/lustre/obdfilter/Makefile.in +++ b/lustre/obdfilter/Makefile.in @@ -1,5 +1,5 @@ MODULES := obdfilter -MDS := @top_srcdir@/lustre/mds/ + obdfilter-objs := filter.o filter_io.o filter_log.o filter_san.o obdfilter-objs += lproc_obdfilter.o filter_lvb.o @@ -7,7 +7,6 @@ ifeq ($(PATCHLEVEL),4) obdfilter-objs += filter_io_24.o else obdfilter-objs += filter_io_26.o -obdfilter-objs += $(MDS)quota_context.o endif # PATCHLEVEL @INCLUDE_RULES@ diff --git a/lustre/obdfilter/autoMakefile.am b/lustre/obdfilter/autoMakefile.am index 09d52b2..5f90afb 100644 --- a/lustre/obdfilter/autoMakefile.am +++ b/lustre/obdfilter/autoMakefile.am @@ -7,5 +7,5 @@ if MODULES modulefs_DATA = obdfilter$(KMODEXT) endif -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ DIST_SOURCES = $(obdfilter-objs:%.o=%.c) filter_io_24.c filter_io_26.c filter_internal.h diff --git a/lustre/obdfilter/filter.c b/lustre/obdfilter/filter.c index 4dcf70e..8117b51 100644 --- a/lustre/obdfilter/filter.c +++ b/lustre/obdfilter/filter.c @@ -42,7 +42,6 @@ #include <linux/init.h> #include <linux/version.h> #include <linux/sched.h> -#include <linux/quotaops.h> #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)) # include <linux/mount.h> # include <linux/buffer_head.h> @@ -62,9 +61,6 @@ static struct lvfs_callback_ops filter_lvfs_ops; -static int filter_destroy(struct obd_export *exp, struct obdo *oa, - struct lov_stripe_md *ea, struct obd_trans_info *); - static void filter_commit_cb(struct obd_device *obd, __u64 transno, void *cb_data, int error) { @@ -110,10 +106,16 @@ int filter_finish_transno(struct obd_export *exp, struct obd_trans_info *oti, fcd->fcd_last_xid = 0; off = fed->fed_lr_off; - fsfilt_add_journal_cb(exp->exp_obd, last_rcvd, oti->oti_handle, - filter_commit_cb, NULL); - err = fsfilt_write_record(exp->exp_obd, filter->fo_rcvd_filp, fcd, - sizeof(*fcd), &off, 0); + if (off <= 0) { + CERROR("%s: client idx %d is %lld\n", exp->exp_obd->obd_name, + fed->fed_lr_idx, fed->fed_lr_off); + err = -EINVAL; + } else { + fsfilt_add_journal_cb(exp->exp_obd, last_rcvd, oti->oti_handle, + filter_commit_cb, NULL); + err = fsfilt_write_record(exp->exp_obd, filter->fo_rcvd_filp, + fcd, sizeof(*fcd), &off, 0); + } if (err) { log_pri = D_ERROR; if (rc == 0) @@ -148,6 +150,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter, ENTRY; LASSERT(bitmap != NULL); + LASSERTF(cl_idx > -2, "%d\n", cl_idx); /* XXX if fcd_uuid were a real obd_uuid, I could use obd_uuid_equals */ if (!strcmp(fed->fed_fcd->fcd_uuid, obd->obd_uuid.uuid)) @@ -161,7 +164,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter, repeat: if (cl_idx >= FILTER_LR_MAX_CLIENTS) { CERROR("no client slots - fix FILTER_LR_MAX_CLIENTS\n"); - RETURN(-ENOMEM); + RETURN(-EOVERFLOW); } if (test_and_set_bit(cl_idx, bitmap)) { cl_idx = find_next_zero_bit(bitmap, @@ -180,12 +183,13 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter, fed->fed_lr_idx = cl_idx; fed->fed_lr_off = le32_to_cpu(filter->fo_fsd->fsd_client_start) + cl_idx * le16_to_cpu(filter->fo_fsd->fsd_client_size); + LASSERTF(fed->fed_lr_off > 0, "fed_lr_off = %llu\n", fed->fed_lr_off); CDEBUG(D_INFO, "client at index %d (%llu) with UUID '%s' added\n", fed->fed_lr_idx, fed->fed_lr_off, fed->fed_fcd->fcd_uuid); if (new_client) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; loff_t off = fed->fed_lr_off; int err; void *handle; @@ -193,7 +197,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter, CDEBUG(D_INFO, "writing client fcd at idx %u (%llu) (len %u)\n", fed->fed_lr_idx,off,(unsigned int)sizeof(*fed->fed_fcd)); - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); /* Transaction needed to fix bug 1403 */ handle = fsfilt_start(obd, filter->fo_rcvd_filp->f_dentry->d_inode, @@ -210,7 +214,7 @@ static int filter_client_add(struct obd_device *obd, struct filter_obd *filter, filter->fo_rcvd_filp->f_dentry->d_inode, handle, 1); } - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (err) { CERROR("error writing %s client idx %u: rc %d\n", @@ -227,7 +231,7 @@ static int filter_client_free(struct obd_export *exp) struct filter_obd *filter = &exp->exp_obd->u.filter; struct obd_device *obd = exp->exp_obd; struct filter_client_data zero_fcd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int rc; loff_t off; ENTRY; @@ -235,19 +239,24 @@ static int filter_client_free(struct obd_export *exp) if (fed->fed_fcd == NULL) RETURN(0); - if (exp->exp_flags & OBD_OPT_FAILOVER) - GOTO(free, 0); - /* XXX if fcd_uuid were a real obd_uuid, I could use obd_uuid_equals */ if (strcmp(fed->fed_fcd->fcd_uuid, obd->obd_uuid.uuid ) == 0) GOTO(free, 0); + CDEBUG(D_INFO, "freeing client at idx %u, offset %lld with UUID '%s'\n", + fed->fed_lr_idx, off, fed->fed_fcd->fcd_uuid); + LASSERT(filter->fo_last_rcvd_slots != NULL); off = fed->fed_lr_off; - CDEBUG(D_INFO, "freeing client at idx %u (%lld) with UUID '%s'\n", - fed->fed_lr_idx, fed->fed_lr_off, fed->fed_fcd->fcd_uuid); + /* Don't clear fed_lr_idx here as it is likely also unset. At worst + * we leak a client slot that will be cleaned on the next recovery. */ + if (off <= 0) { + CERROR("%s: client idx %d has med_off %lld\n", + obd->obd_name, fed->fed_lr_idx, off); + GOTO(free, rc = -EINVAL); + } /* Clear the bit _after_ zeroing out the client so we don't race with filter_client_add and zero out new clients.*/ @@ -257,21 +266,23 @@ static int filter_client_free(struct obd_export *exp) LBUG(); } - memset(&zero_fcd, 0, sizeof zero_fcd); - push_ctxt(&saved, &obd->obd_ctxt, NULL); - rc = fsfilt_write_record(obd, filter->fo_rcvd_filp, &zero_fcd, - sizeof(zero_fcd), &off, 0); + if (!(exp->exp_flags & OBD_OPT_FAILOVER)) { + memset(&zero_fcd, 0, sizeof zero_fcd); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); + rc = fsfilt_write_record(obd, filter->fo_rcvd_filp, &zero_fcd, + sizeof(zero_fcd), &off, 0); - if (rc == 0) - /* update server's transno */ - filter_update_server_data(obd, filter->fo_rcvd_filp, - filter->fo_fsd, 1); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); - - CDEBUG(rc == 0 ? D_INFO : D_ERROR, - "zeroing disconnecting client %s at idx %u (%llu) in %s rc %d\n", - fed->fed_fcd->fcd_uuid, fed->fed_lr_idx, fed->fed_lr_off, - LAST_RCVD, rc); + if (rc == 0) + /* update server's transno */ + filter_update_server_data(obd, filter->fo_rcvd_filp, + filter->fo_fsd, 1); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); + + CDEBUG(rc == 0 ? D_INFO : D_ERROR, + "zeroing out client %s at idx %u (%llu) in %s rc %d\n", + fed->fed_fcd->fcd_uuid, fed->fed_lr_idx, fed->fed_lr_off, + LAST_RCVD, rc); + } if (!test_and_clear_bit(fed->fed_lr_idx, filter->fo_last_rcvd_slots)) { CERROR("FILTER client %u: bit already clear in bitmap!!\n", @@ -279,10 +290,12 @@ static int filter_client_free(struct obd_export *exp) LBUG(); } + EXIT; free: OBD_FREE(fed->fed_fcd, sizeof(*fed->fed_fcd)); + fed->fed_fcd = NULL; - RETURN(0); + return 0; } static int filter_free_server_data(struct filter_obd *filter) @@ -475,7 +488,9 @@ static int filter_init_server_data(struct obd_device *obd, struct file * filp) sizeof exp->exp_client_uuid.uuid); fed = &exp->exp_filter_data; fed->fed_fcd = fcd; - filter_client_add(obd, filter, fed, cl_idx); + rc = filter_client_add(obd, filter, fed, cl_idx); + LASSERTF(rc == 0, "rc = %d\n", rc); /* can't fail existing */ + /* create helper if export init gets more complex */ spin_lock_init(&fed->fed_lock); @@ -741,14 +756,14 @@ static int filter_prep_groups(struct obd_device *obd) /* setup the object store with correct subdirectories */ static int filter_prep(struct obd_device *obd) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct filter_obd *filter = &obd->u.filter; struct file *file; struct inode *inode; int rc = 0; ENTRY; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); file = filp_open(LAST_RCVD, O_RDWR | O_CREAT | O_LARGEFILE, 0700); if (!file || IS_ERR(file)) { rc = PTR_ERR(file); @@ -781,7 +796,7 @@ static int filter_prep(struct obd_device *obd) GOTO(err_server_data, rc); out: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); return(rc); @@ -798,7 +813,7 @@ static int filter_prep(struct obd_device *obd) /* cleanup the filter: write last used object id to status file */ static void filter_post(struct obd_device *obd) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct filter_obd *filter = &obd->u.filter; int rc, i; @@ -806,7 +821,7 @@ static void filter_post(struct obd_device *obd) * best to start a transaction with h_sync, because we removed this * from lastobjid */ - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); rc = filter_update_server_data(obd, filter->fo_rcvd_filp, filter->fo_fsd, 0); if (rc) @@ -826,7 +841,7 @@ static void filter_post(struct obd_device *obd) filter_cleanup_groups(obd); filter_free_server_data(filter); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); } static void filter_set_last_id(struct filter_obd *filter, struct obdo *oa, @@ -1182,7 +1197,7 @@ static int filter_intent_policy(struct ldlm_namespace *ns, /* mount the file system (secretly) */ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, - char *option) + void *option) { struct lustre_cfg* lcfg = buf; struct filter_obd *filter = &obd->u.filter; @@ -1201,13 +1216,13 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, if (IS_ERR(obd->obd_fsops)) RETURN(PTR_ERR(obd->obd_fsops)); - mnt = do_kern_mount(lustre_cfg_string(lcfg, 2), MS_NOATIME | MS_NODIRATIME, - lustre_cfg_string(lcfg, 1), (void *)option); + mnt = do_kern_mount(lustre_cfg_string(lcfg, 2),MS_NOATIME|MS_NODIRATIME, + lustre_cfg_string(lcfg, 1), option); rc = PTR_ERR(mnt); if (IS_ERR(mnt)) GOTO(err_ops, rc); - LASSERT(!ll_check_rdonly(ll_sbdev(mnt->mnt_sb))); + LASSERT(!lvfs_check_rdonly(lvfs_sbdev(mnt->mnt_sb))); if (lcfg->lcfg_bufcount > 3 && LUSTRE_CFG_BUFLEN(lcfg, 3) > 0) { str = lustre_cfg_string(lcfg, 3); @@ -1230,11 +1245,11 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, filter->fo_fstype = mnt->mnt_sb->s_type->name; CDEBUG(D_SUPER, "%s: mnt = %p\n", filter->fo_fstype, mnt); - OBD_SET_CTXT_MAGIC(&obd->obd_ctxt); - obd->obd_ctxt.pwdmnt = mnt; - obd->obd_ctxt.pwd = mnt->mnt_root; - obd->obd_ctxt.fs = get_ds(); - obd->obd_ctxt.cb_ops = filter_lvfs_ops; + OBD_SET_CTXT_MAGIC(&obd->obd_lvfs_ctxt); + obd->obd_lvfs_ctxt.pwdmnt = mnt; + obd->obd_lvfs_ctxt.pwd = mnt->mnt_root; + obd->obd_lvfs_ctxt.fs = get_ds(); + obd->obd_lvfs_ctxt.cb_ops = filter_lvfs_ops; rc = filter_prep(obd); if (rc) @@ -1262,8 +1277,6 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, spin_lock_init(&filter->fo_w_disk_iosize.oh_lock); filter->fo_readcache_max_filesize = FILTER_MAX_CACHE_SIZE; - atomic_set(&filter->fo_quotachecking, 1); - sprintf(ns_name, "filter-%s", obd->obd_uuid.uuid); obd->obd_namespace = ldlm_namespace_new(ns_name, LDLM_NAMESPACE_SERVER); if (obd->obd_namespace == NULL) @@ -1281,6 +1294,11 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, GOTO(err_post, rc); } + rc = filter_quota_setup(filter); + if (rc) { + GOTO(err_post, rc); + } + if (obd->obd_recovering) { LCONSOLE_WARN("OST %s now serving %s, but will be in recovery " "until %d %s reconnect, or if no clients " @@ -1303,13 +1321,6 @@ int filter_common_setup(struct obd_device *obd, obd_count len, void *buf, obd->obd_replayable ? "enabled" : "disabled"); } - rc = qctxt_init(&filter->fo_quota_ctxt, filter->fo_sb, NULL); - if (rc) { - CERROR("initialize quota context failed! (rc:%d)\n", rc); - qctxt_cleanup(&filter->fo_quota_ctxt, 0); - GOTO(err_post, rc); - } - RETURN(0); err_post: @@ -1328,12 +1339,21 @@ static int filter_setup(struct obd_device *obd, obd_count len, void *buf) { struct lprocfs_static_vars lvars; struct lustre_cfg* lcfg = buf; + unsigned long page; int rc; if (!LUSTRE_CFG_BUFLEN(lcfg, 1) || !LUSTRE_CFG_BUFLEN(lcfg, 2)) RETURN(-EINVAL); - rc = filter_common_setup(obd, len, buf, lustre_cfg_buf(lcfg, 4)); + /* 2.6.9 selinux wants a full option page for do_kern_mount (bug6471) */ + page = get_zeroed_page(GFP_KERNEL); + if (!page) + RETURN(-ENOMEM); + + memcpy((void *)page, lustre_cfg_buf(lcfg, 4), + LUSTRE_CFG_BUFLEN(lcfg, 4)); + rc = filter_common_setup(obd, len, buf, (void *)page); + free_page(page); lprocfs_init_vars(filter, &lvars); if (rc == 0 && lprocfs_obd_setup(obd, lvars.obd_vars) == 0 && @@ -1349,19 +1369,82 @@ static int filter_setup(struct obd_device *obd, obd_count len, void *buf) lproc_filter_attach_seqstat(obd); } + ping_evictor_start(); + return rc; } -static int filter_precleanup(struct obd_device *obd) +static struct llog_operations filter_mds_ost_repl_logops /* initialized below*/; +static struct llog_operations filter_size_orig_logops = { + lop_setup: llog_obd_origin_setup, + lop_cleanup: llog_obd_origin_cleanup, + lop_add: llog_obd_origin_add +}; + +static int filter_llog_init(struct obd_device *obd, struct obd_device *tgt, + int count, struct llog_catid *catid) { - target_cleanup_recovery(obd); - return (0); + struct llog_ctxt *ctxt; + int rc; + ENTRY; + + filter_mds_ost_repl_logops = llog_client_ops; + filter_mds_ost_repl_logops.lop_cancel = llog_obd_repl_cancel; + filter_mds_ost_repl_logops.lop_connect = llog_repl_connect; + filter_mds_ost_repl_logops.lop_sync = llog_obd_repl_sync; + + rc = llog_setup(obd, LLOG_MDS_OST_REPL_CTXT, tgt, 0, NULL, + &filter_mds_ost_repl_logops); + if (rc) + RETURN(rc); + + /* FIXME - assign unlink_cb for filter's recovery */ + ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT); + ctxt->llog_proc_cb = filter_recov_log_mds_ost_cb; + + rc = llog_setup(obd, LLOG_SIZE_ORIG_CTXT, tgt, 0, NULL, + &filter_size_orig_logops); + RETURN(rc); +} + +static int filter_llog_finish(struct obd_device *obd, int count) +{ + struct llog_ctxt *ctxt; + int rc = 0, rc2 = 0; + ENTRY; + + ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT); + if (ctxt) + rc = llog_cleanup(ctxt); + + ctxt = llog_get_context(obd, LLOG_SIZE_ORIG_CTXT); + if (ctxt) + rc2 = llog_cleanup(ctxt); + if (!rc) + rc = rc2; + + RETURN(rc); +} + +static int filter_precleanup(struct obd_device *obd, int stage) +{ + int rc = 0; + ENTRY; + + switch(stage) { + case 1: + target_cleanup_recovery(obd); + break; + case 2: + rc = filter_llog_finish(obd, 0); + } + RETURN(rc); } static int filter_cleanup(struct obd_device *obd) { struct filter_obd *filter = &obd->u.filter; - ll_sbdev_type save_dev; + lvfs_sbdev_type save_dev; int must_relock = 0; ENTRY; @@ -1378,13 +1461,15 @@ static int filter_cleanup(struct obd_device *obd) } } - qctxt_cleanup(&filter->fo_quota_ctxt, 0); + ping_evictor_stop(); + + filter_quota_cleanup(filter); ldlm_namespace_free(obd->obd_namespace, obd->obd_force); if (filter->fo_sb == NULL) RETURN(0); - save_dev = ll_sbdev(filter->fo_sb); + save_dev = lvfs_sbdev(filter->fo_sb); lprocfs_free_obd_stats(obd); lprocfs_obd_cleanup(obd); @@ -1392,8 +1477,8 @@ static int filter_cleanup(struct obd_device *obd) filter_post(obd); shrink_dcache_parent(filter->fo_sb->s_root); - - DQUOT_OFF(filter->fo_sb); + + LL_DQUOT_OFF(filter->fo_sb); if (atomic_read(&filter->fo_vfsmnt->mnt_count) > 1) CERROR("%s: mount point %p busy, mnt_count: %d\n", @@ -1407,14 +1492,11 @@ static int filter_cleanup(struct obd_device *obd) must_relock++; } - obd_llog_finish(obd, 0); - mntput(filter->fo_vfsmnt); //destroy_buffers(filter->fo_sb->s_dev); filter->fo_sb = NULL; - - ll_clear_rdonly(save_dev); + lvfs_clear_rdonly(save_dev); if (must_relock) lock_kernel(); @@ -1428,7 +1510,7 @@ static int filter_cleanup(struct obd_device *obd) /* nearly identical to mds_connect */ static int filter_connect(struct lustre_handle *conn, struct obd_device *obd, - struct obd_uuid *cluuid, struct obd_connect_data *data) + struct obd_uuid *cluuid,struct obd_connect_data *data) { struct obd_export *exp; struct filter_export_data *fed; @@ -1447,8 +1529,11 @@ static int filter_connect(struct lustre_handle *conn, struct obd_device *obd, LASSERT(exp != NULL); fed = &exp->exp_filter_data; - data->ocd_connect_flags &= OST_CONNECT_SUPPORTED; - exp->exp_connect_flags = data->ocd_connect_flags; + + if (data != NULL) { + data->ocd_connect_flags &= OST_CONNECT_SUPPORTED; + exp->exp_connect_flags = data->ocd_connect_flags; + } spin_lock_init(&fed->fed_lock); @@ -1465,6 +1550,7 @@ static int filter_connect(struct lustre_handle *conn, struct obd_device *obd, fed->fed_fcd = fcd; rc = filter_client_add(obd, filter, fed, -1); + GOTO(cleanup, rc); cleanup: if (rc) { @@ -1476,7 +1562,7 @@ cleanup: } else { class_export_put(exp); } - + RETURN(rc); } @@ -1640,7 +1726,7 @@ static int filter_disconnect(struct obd_export *exp) } struct dentry *__filter_oa2dentry(struct obd_device *obd, - struct obdo *oa, const char *what) + struct obdo *oa, const char *what, int quiet) { struct dentry *dchild = NULL; obd_gr group = 0; @@ -1656,8 +1742,9 @@ struct dentry *__filter_oa2dentry(struct obd_device *obd, } if (dchild->d_inode == NULL) { - CERROR("%s: %s on non-existent object: "LPU64"\n", - obd->obd_name, what, oa->o_id); + if (!quiet) + CERROR("%s: %s on non-existent object: "LPU64"\n", + obd->obd_name, what, oa->o_id); f_dput(dchild); RETURN(ERR_PTR(-ENOENT)); } @@ -1693,11 +1780,11 @@ static int filter_getattr(struct obd_export *exp, struct obdo *oa, } /* this is called from filter_truncate() until we have filter_punch() */ -static int filter_setattr(struct obd_export *exp, struct obdo *oa, - struct lov_stripe_md *md, struct obd_trans_info *oti) +int filter_setattr(struct obd_export *exp, struct obdo *oa, + struct lov_stripe_md *md, struct obd_trans_info *oti) { struct obd_device *obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct filter_obd *filter; struct dentry *dentry; struct iattr iattr; @@ -1710,7 +1797,7 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa, int rc, rc2; ENTRY; - dentry = filter_oa2dentry(exp->exp_obd, oa); + dentry = __filter_oa2dentry(exp->exp_obd, oa, __FUNCTION__, 1); if (IS_ERR(dentry)) RETURN(PTR_ERR(dentry)); @@ -1719,7 +1806,7 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa, iattr_from_obdo(&iattr, oa, oa->o_valid); - push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); lock_kernel(); if (oa->o_valid & OBD_MD_FLCOOKIE) { @@ -1744,24 +1831,18 @@ static int filter_setattr(struct obd_export *exp, struct obdo *oa, if (IS_ERR(handle)) GOTO(out_unlock, rc = PTR_ERR(handle)); - /* XXX this could be a rwsem instead, if filter_preprw played along */ - if (iattr.ia_valid & ATTR_ATTR_FLAG) + if (iattr.ia_valid & ATTR_ATTR_FLAG) { rc = fsfilt_iocontrol(exp->exp_obd, dentry->d_inode, NULL, EXT3_IOC_SETFLAGS, (long)&iattr.ia_attr_flags); - else { + } else { rc = fsfilt_setattr(exp->exp_obd, dentry, handle, &iattr, 1); - /* set cancel cookie callback function */ - if (fcc != NULL) { - if (oti != NULL) - fsfilt_add_journal_cb(obd, 0, oti->oti_handle, - filter_cancel_cookies_cb, - fcc); - else - fsfilt_add_journal_cb(obd, 0, handle, - filter_cancel_cookies_cb, - fcc); - } + if (fcc != NULL) + /* set cancel cookie callback function */ + fsfilt_add_journal_cb(obd, 0, oti ? + oti->oti_handle : handle, + filter_cancel_cookies_cb, + fcc); } rc = filter_finish_transno(exp, oti, rc); @@ -1794,7 +1875,7 @@ out_unlock: if (iattr.ia_valid & ATTR_SIZE) up(&dentry->d_inode->i_sem); unlock_kernel(); - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); f_dput(dentry); @@ -2145,7 +2226,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa, struct lov_stripe_md **ea, struct obd_trans_info *oti) { struct obd_device *obd = NULL; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct lov_stripe_md *lsm = NULL; obd_gr group = 0; int rc = 0, diff; @@ -2166,7 +2247,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa, } obd = exp->exp_obd; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if ((oa->o_valid & OBD_MD_FLFLAGS) && (oa->o_flags & OBD_FL_RECREATE_OBJS)) { @@ -2188,7 +2269,7 @@ static int filter_create(struct obd_export *exp, struct obdo *oa, } } - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); if (rc && ea != NULL && *ea != lsm) { obd_free_memmd(exp, &lsm); } else if (rc == 0 && ea != NULL) { @@ -2203,13 +2284,13 @@ static int filter_create(struct obd_export *exp, struct obdo *oa, RETURN(rc); } -static int filter_destroy(struct obd_export *exp, struct obdo *oa, - struct lov_stripe_md *ea, struct obd_trans_info *oti) +int filter_destroy(struct obd_export *exp, struct obdo *oa, + struct lov_stripe_md *md, struct obd_trans_info *oti) { struct obd_device *obd; struct filter_obd *filter; struct dentry *dchild = NULL, *dparent = NULL; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; void *handle = NULL; struct llog_cookie *fcc = NULL; int rc, rc2, cleanup_phase = 0, have_prepared = 0; @@ -2222,7 +2303,7 @@ static int filter_destroy(struct obd_export *exp, struct obdo *oa, obd = exp->exp_obd; filter = &obd->u.filter; - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); acquire_locks: dparent = filter_parent_lock(obd, group, oa->o_id); @@ -2288,14 +2369,9 @@ cleanup: switch(cleanup_phase) { case 3: if (fcc != NULL) { - if (oti != NULL) - fsfilt_add_journal_cb(obd, 0, oti->oti_handle, - filter_cancel_cookies_cb, - fcc); - else - fsfilt_add_journal_cb(obd, 0, handle, - filter_cancel_cookies_cb, - fcc); + fsfilt_add_journal_cb(obd, 0, + oti ? oti->oti_handle : handle, + filter_cancel_cookies_cb, fcc); } rc = filter_finish_transno(exp, oti, rc); rc2 = fsfilt_commit(obd, dparent->d_inode, handle, 0); @@ -2309,7 +2385,7 @@ cleanup: case 1: filter_parent_unlock(dparent); case 0: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); break; default: CERROR("invalid cleanup_phase %d\n", cleanup_phase); @@ -2350,7 +2426,7 @@ static int filter_truncate(struct obd_export *exp, struct obdo *oa, static int filter_sync(struct obd_export *exp, struct obdo *oa, struct lov_stripe_md *lsm, obd_off start, obd_off end) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct filter_obd *filter; struct dentry *dentry; struct llog_ctxt *ctxt; @@ -2372,10 +2448,10 @@ static int filter_sync(struct obd_export *exp, struct obdo *oa, if (IS_ERR(dentry)) RETURN(PTR_ERR(dentry)); - push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); down(&dentry->d_inode->i_sem); - rc = filemap_fdatasync(dentry->d_inode->i_mapping); + rc = filemap_fdatawrite(dentry->d_inode->i_mapping); if (rc == 0) { /* just any file to grab fsync method - "file" arg unused */ struct file *file = filter->fo_rcvd_filp; @@ -2392,7 +2468,7 @@ static int filter_sync(struct obd_export *exp, struct obdo *oa, oa->o_valid = OBD_MD_FLID; obdo_from_inode(oa, dentry->d_inode, FILTER_VALID_FLAGS); - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); f_dput(dentry); RETURN(rc); @@ -2467,8 +2543,7 @@ static int filter_set_info(struct obd_export *exp, __u32 keylen, ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT); rc = llog_receptor_accept(ctxt, exp->exp_imp_reverse); - /* setup the quota context import */ - obd->u.filter.fo_quota_ctxt.lqc_import = exp->exp_imp_reverse; + filter_quota_set_info(exp, obd); RETURN(rc); } @@ -2498,7 +2573,7 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp, struct super_block *sb = obd->u.filter.fo_sb; struct inode *inode = sb->s_root->d_inode; BDEVNAME_DECLARE_STORAGE(tmp); - CERROR("setting device %s read-only\n", + CERROR("*** setting device %s read-only ***\n", ll_bdevname(sb, tmp)); handle = fsfilt_start(obd, inode, FSFILT_OP_MKNOD, NULL); @@ -2508,7 +2583,7 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp, CDEBUG(D_HA, "syncing ost %s\n", obd->obd_name); rc = fsfilt_sync(obd, obd->u.filter.fo_sb); - ll_set_rdonly(ll_sbdev(obd->u.filter.fo_sb)); + lvfs_set_rdonly(lvfs_sbdev(obd->u.filter.fo_sb)); RETURN(0); } @@ -2526,9 +2601,9 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp, /* struct llog_ctxt *ctxt = NULL; - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); rc = llog_ioctl(ctxt, cmd, data); - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); RETURN(rc); */ @@ -2541,119 +2616,6 @@ int filter_iocontrol(unsigned int cmd, struct obd_export *exp, RETURN(0); } -static int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct obd_run_ctxt saved; - struct obd_device *obd = exp->exp_obd; - int rc; - - push_ctxt(&saved, &obd->obd_ctxt, NULL); - - rc = fsfilt_quotacheck(obd, obd->u.filter.fo_sb, oqctl); - if (rc) - CERROR("%s: fsfilt_quotacheck: %d\n", obd->obd_name, rc); - - pop_ctxt(&saved, &obd->obd_ctxt, NULL); - - RETURN(rc); -} - -static int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct obd_device *obd = exp->exp_obd; - struct obd_run_ctxt saved; - int rc = 0; - ENTRY; - - if (oqctl->qc_cmd == Q_QUOTAON || oqctl->qc_cmd == Q_QUOTAOFF || - oqctl->qc_cmd == Q_GETOINFO || oqctl->qc_cmd == Q_GETOQUOTA || - oqctl->qc_cmd == Q_GETQUOTA) { - push_ctxt(&saved, &obd->obd_ctxt, NULL); - rc = fsfilt_quotactl(obd, obd->u.filter.fo_sb, oqctl); - pop_ctxt(&saved, &obd->obd_ctxt, NULL); - } else if (oqctl->qc_cmd == Q_INITQUOTA) { - unsigned int uid = 0, gid = 0; - - /* initialize quota limit to MIN_QLIMIT */ - LASSERT(oqctl->qc_dqblk.dqb_valid == QIF_BLIMITS); - LASSERT(oqctl->qc_dqblk.dqb_bhardlimit == MIN_QLIMIT); - LASSERT(oqctl->qc_dqblk.dqb_bsoftlimit == 0); - oqctl->qc_cmd = Q_SETQUOTA; - rc = fsfilt_quotactl(obd, obd->u.filter.fo_sb, oqctl); - /* this value will be replied to client, we must restore it */ - oqctl->qc_cmd = Q_INITQUOTA; - if (rc) - RETURN(rc); - - /* trigger qunit pre-acquire */ - if (oqctl->qc_type == USRQUOTA) - uid = oqctl->qc_id; - else - gid = oqctl->qc_id; - - rc = qctxt_adjust_qunit(obd, &obd->u.filter.fo_quota_ctxt, - uid, gid, 1); - } else { - CERROR("%s: unsupported filter_quotactl command: %d\n", - obd->obd_name, oqctl->qc_cmd); - LBUG(); - } - - RETURN(rc); -} - -static struct llog_operations filter_mds_ost_repl_logops; -static struct llog_operations filter_size_orig_logops = { - lop_setup: llog_obd_origin_setup, - lop_cleanup: llog_obd_origin_cleanup, - lop_add: llog_obd_origin_add -}; - -static int filter_llog_init(struct obd_device *obd, struct obd_device *tgt, - int count, struct llog_catid *catid) -{ - struct llog_ctxt *ctxt; - int rc; - ENTRY; - - filter_mds_ost_repl_logops = llog_client_ops; - filter_mds_ost_repl_logops.lop_cancel = llog_obd_repl_cancel; - filter_mds_ost_repl_logops.lop_connect = llog_repl_connect; - filter_mds_ost_repl_logops.lop_sync = llog_obd_repl_sync; - - rc = llog_setup(obd, LLOG_MDS_OST_REPL_CTXT, tgt, 0, NULL, - &filter_mds_ost_repl_logops); - if (rc) - RETURN(rc); - - /* FIXME - assign unlink_cb for filter's recovery */ - ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT); - ctxt->llog_proc_cb = filter_recov_log_mds_ost_cb; - - rc = llog_setup(obd, LLOG_SIZE_ORIG_CTXT, tgt, 0, NULL, - &filter_size_orig_logops); - RETURN(rc); -} - -static int filter_llog_finish(struct obd_device *obd, int count) -{ - struct llog_ctxt *ctxt; - int rc = 0, rc2 = 0; - ENTRY; - - ctxt = llog_get_context(obd, LLOG_MDS_OST_REPL_CTXT); - if (ctxt) - rc = llog_cleanup(ctxt); - - ctxt = llog_get_context(obd, LLOG_SIZE_ORIG_CTXT); - if (ctxt) - rc2 = llog_cleanup(ctxt); - if (!rc) - rc = rc2; - - RETURN(rc); -} - static struct dentry *filter_lvfs_fid2dentry(__u64 id, __u32 gen, __u64 gr, void *data) { diff --git a/lustre/obdfilter/filter_internal.h b/lustre/obdfilter/filter_internal.h index e90fb60..1c30fbc 100644 --- a/lustre/obdfilter/filter_internal.h +++ b/lustre/obdfilter/filter_internal.h @@ -100,8 +100,8 @@ void f_dput(struct dentry *); struct dentry *filter_fid2dentry(struct obd_device *, struct dentry *dir, obd_gr group, obd_id id); struct dentry *__filter_oa2dentry(struct obd_device *obd, struct obdo *oa, - const char *what); -#define filter_oa2dentry(obd, oa) __filter_oa2dentry(obd, oa, __FUNCTION__) + const char *what, int quiet); +#define filter_oa2dentry(obd, oa) __filter_oa2dentry(obd, oa, __FUNCTION__, 0) int filter_finish_transno(struct obd_export *, struct obd_trans_info *, int rc); __u64 filter_next_id(struct filter_obd *, struct obdo *); @@ -110,7 +110,11 @@ int filter_update_server_data(struct obd_device *, struct file *, struct filter_server_data *, int force_sync); int filter_update_last_objid(struct obd_device *, obd_gr, int force_sync); int filter_common_setup(struct obd_device *, obd_count len, void *buf, - char *option); + void *option); +int filter_destroy(struct obd_export *exp, struct obdo *oa, + struct lov_stripe_md *md, struct obd_trans_info *); +int filter_setattr(struct obd_export *exp, struct obdo *oa, + struct lov_stripe_md *md, struct obd_trans_info *oti); /* filter_lvb.c */ extern struct ldlm_valblock_ops filter_lvbo; @@ -168,22 +172,83 @@ int filter_san_setup(struct obd_device *obd, obd_count len, void *buf); int filter_san_preprw(int cmd, struct obd_export *, struct obdo *, int objcount, struct obd_ioobj *, int niocount, struct niobuf_remote *); -#ifdef __KERNEL__ +#ifdef LPROCFS void filter_tally_write(struct filter_obd *filter, struct page **pages, - int nr_pages, unsigned long *blocks, + int nr_pages, unsigned long *blocks, int blocks_per_page); -void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb, +void filter_tally_read(struct filter_obd *filter, struct niobuf_local *lnb, int niocount); int lproc_filter_attach_seqstat(struct obd_device *dev); #else -static inline filter_tally_write(struct filter_obd *filter, - struct page **pages, int nr_pages, +static inline void filter_tally_write(struct filter_obd *filter, + struct page **pages, int nr_pages, unsigned long *blocks, int blocks_per_page) {} -static inline void filter_tally_read(struct filter_obd *filter, - struct niobuf_local *lnb, int niocount) - {} -static inline lproc_filter_attach_seqstat(struct obd_device *dev) {} +static inline void filter_tally_read(struct filter_obd *filter, + struct niobuf_local *lnb, int niocount) {} +static inline int lproc_filter_attach_seqstat(struct obd_device *dev) {}; #endif +/* Quota stuff */ +#ifdef HAVE_QUOTA_SUPPORT +int filter_quota_setup(struct filter_obd *filter); +void filter_quota_cleanup(struct filter_obd *filter); +void filter_quota_set_info(struct obd_export *exp, struct obd_device *obd); +int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl); +int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl); +int filter_quota_enforcement(struct obd_device *obd, + unsigned int fsuid, unsigned int fsgid, + struct lvfs_ucred **ret_uc); +int filter_get_quota_flag(struct obd_device *obd, struct obdo *oa); +int filter_quota_check_master(struct obd_device *obd, struct inode *inode); + +#ifdef LPROCFS +int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, + int count, int *eof, void *data); +int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, + int count, int *eof, void *data); +int lprocfs_filter_wr_bunit(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_filter_wr_iunit(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_filter_rd_btune(char *page, char **start, off_t off, + int count, int *eof, void *data); +int lprocfs_filter_rd_itune(char *page, char **start, off_t off, + int count, int *eof, void *data); +int lprocfs_filter_wr_btune(struct file *file, const char *buffer, + unsigned long count, void *data); +int lprocfs_filter_wr_itune(struct file *file, const char *buffer, + unsigned long count, void *data); +#endif /* LPROCFS */ +#else /* Quota */ +static inline int filter_quota_setup(struct filter_obd *filter) +{ + return 0; +} +static inline void filter_quota_cleanup(struct filter_obd *filter) {} +static inline void filter_quota_set_info(struct obd_export *exp, struct obd_device *obd) {} +static inline int filter_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} +static inline int filter_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} +static inline int filter_quota_enforcement(struct obd_device *obd, + unsigned int fsuid, unsigned int fsgid, + struct lvfs_ucred **ret_uc) +{ + return 0; +} +static inline int filter_get_quota_flag(struct obd_device *obd, + struct obdo *oa) +{ + return 0; +} +static inline int filter_quota_check_master(struct obd_device *obd, struct inode *inode) +{ + return 0; +} +#endif /* Quota */ #endif diff --git a/lustre/obdfilter/filter_io.c b/lustre/obdfilter/filter_io.c index baa8173..88e2dfa 100644 --- a/lustre/obdfilter/filter_io.c +++ b/lustre/obdfilter/filter_io.c @@ -272,7 +272,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa, struct obd_trans_info *oti) { struct obd_device *obd = exp->exp_obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct niobuf_remote *rnb; struct niobuf_local *lnb; struct dentry *dentry = NULL; @@ -297,7 +297,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa, memset(res, 0, niocount * sizeof(*res)); - push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); rc = filter_alloc_iobuf(&obd->u.filter, OBD_BRW_READ, obj->ioo_bufcnt, &iobuf); @@ -318,7 +318,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa, fsfilt_check_slow(now, obd_timeout, "preprw_read setup"); - for (i = 0, lnb = res, rnb = nb; i < obj->ioo_bufcnt; + for (i = 0, lnb = res, rnb = nb; i < obj->ioo_bufcnt; i++, rnb++, lnb++) { lnb->dentry = dentry; lnb->offset = rnb->offset; @@ -327,8 +327,8 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa, if (inode->i_size <= rnb->offset) /* If there's no more data, abort early. - * lnb->page == NULL and lnb->rc == 0, so it's - * easy to detect later. */ + * lnb->page == NULL and lnb->rc == 0, so it's + * easy to detect later. */ break; else rc = filter_alloc_dio_page(obd, inode, lnb); @@ -375,7 +375,7 @@ static int filter_preprw_read(int cmd, struct obd_export *exp, struct obdo *oa, if (iobuf != NULL) filter_free_iobuf(iobuf); - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); if (rc) CERROR("io error %d\n", rc); @@ -512,7 +512,7 @@ static int filter_preprw_write(int cmd, struct obd_export *exp, struct obdo *oa, struct niobuf_local *res, struct obd_trans_info *oti) { - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct niobuf_remote *rnb; struct niobuf_local *lnb = res; struct fsfilt_objinfo fso; @@ -527,13 +527,14 @@ static int filter_preprw_write(int cmd, struct obd_export *exp, struct obdo *oa, memset(res, 0, niocount * sizeof(*res)); + /* This iobuf is for reading any partial pages from disk */ rc = filter_alloc_iobuf(&exp->exp_obd->u.filter, OBD_BRW_READ, obj->ioo_bufcnt, &iobuf); if (rc) GOTO(cleanup, rc); cleanup_phase = 1; - push_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); dentry = filter_fid2dentry(exp->exp_obd, NULL, obj->ioo_gr, obj->ioo_id); if (IS_ERR(dentry)) @@ -648,7 +649,7 @@ cleanup: if (rc) filter_free_dio_pages(objcount, obj, niocount, res); case 3: - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); filter_free_iobuf(iobuf); case 2: if (rc) @@ -659,7 +660,7 @@ cleanup: if (oa) filter_grant_incoming(exp, oa); spin_unlock(&exp->exp_obd->obd_osfs_lock); - pop_ctxt(&saved, &exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &exp->exp_obd->obd_lvfs_ctxt, NULL); filter_free_iobuf(iobuf); break; default:; diff --git a/lustre/obdfilter/filter_io_24.c b/lustre/obdfilter/filter_io_24.c index d4327ca..4d43bb3 100644 --- a/lustre/obdfilter/filter_io_24.c +++ b/lustre/obdfilter/filter_io_24.c @@ -79,7 +79,7 @@ static void check_pending_bhs(unsigned long *blocks, int nr_pages, dev_t dev, /* when brw_kiovec() is asked to read from block -1UL it just zeros * the page. this gives us a chance to verify the write mappings * as well */ -static int filter_cleanup_mappings(int rw, struct kiobuf *iobuf, +static int filter_cleanup_mappings(int rw, struct kiobuf *iobuf, struct inode *inode) { int i, blocks_per_page_bits = PAGE_SHIFT - inode->i_blkbits; @@ -107,23 +107,55 @@ static void dump_page(int rw, unsigned long block, struct page *page) } #endif -static void filter_clear_page_cache(struct inode *inode, struct kiobuf *iobuf) +/* These are our hacks to keep our directio/bh IO coherent with ext3's + * page cache use. Most notably ext3 reads file data into the page + * cache when it is zeroing the tail of partial-block truncates and + * leaves it there, sometimes generating io from it at later truncates. + * This removes the partial page and its buffers from the page cache, + * so it should only ever cause a wait in rare cases, as otherwise we + * always do full-page IO to the OST. + * + * The call to truncate_complete_page() will call journal_flushpage() to + * free the buffers and drop the page from cache. The buffers should not + * be dirty, because we already called fdatasync/fdatawait on them. + */ +static int filter_clear_page_cache(struct inode *inode, struct kiobuf *iobuf) { struct page *page; - int i; + int i, rc, rc2; + + check_pending_bhs(KIOBUF_GET_BLOCKS(iobuf), iobuf->nr_pages, + inode->i_dev, 1 << inode->i_blkbits); + + /* This is nearly generic_osync_inode, without the waiting on the inode + rc = generic_osync_inode(inode, inode->i_mapping, + OSYNC_DATA|OSYNC_METADATA); + */ + rc = filemap_fdatasync(inode->i_mapping); + rc2 = fsync_inode_data_buffers(inode); + if (rc == 0) + rc = rc2; + rc2 = filemap_fdatawait(inode->i_mapping); + if (rc == 0) + rc = rc2; + if (rc != 0) + RETURN(rc); + /* be careful to call this after fsync_inode_data_buffers has waited + * for IO to complete before we evict it from the cache */ for (i = 0; i < iobuf->nr_pages ; i++) { page = find_lock_page(inode->i_mapping, iobuf->maplist[i]->index); if (page == NULL) continue; - if (page->mapping != NULL) { - block_flushpage(page, 0); - truncate_complete_page(page); - } + if (page->mapping != NULL) + ll_truncate_complete_page(page); + unlock_page(page); page_cache_release(page); } + + return 0; } /* Must be called with i_sem taken for writes; this will drop it */ @@ -147,7 +179,7 @@ int filter_direct_io(int rw, struct dentry *dchild, void *buf, if (iobuf->nr_pages * blocks_per_page > KIO_MAX_SECTORS) GOTO(cleanup, rc = -EINVAL); - if (iobuf->nr_pages * blocks_per_page > + if (iobuf->nr_pages * blocks_per_page > OBDFILTER_CREATED_SCRATCHPAD_ENTRIES) GOTO(cleanup, rc = -EINVAL); @@ -196,27 +228,10 @@ int filter_direct_io(int rw, struct dentry *dchild, void *buf, GOTO(cleanup, rc); } - /* these are our hacks to keep our directio/bh IO coherent with ext3's - * page cache use. Most notably ext3 reads file data into the page - * cache when it is zeroing the tail of partial-block truncates and - * leaves it there, sometimes generating io from it at later truncates. - * Someday very soon we'll be performing our brw_kiovec() IO to and - * from the page cache. */ - check_pending_bhs(KIOBUF_GET_BLOCKS(iobuf), iobuf->nr_pages, - inode->i_dev, 1 << inode->i_blkbits); - - rc = filemap_fdatasync(inode->i_mapping); - if (rc == 0) - rc = fsync_inode_data_buffers(inode); - if (rc == 0) - rc = filemap_fdatawait(inode->i_mapping); + rc = filter_clear_page_cache(inode, iobuf); if (rc < 0) GOTO(cleanup, rc); - /* be careful to call this after fsync_inode_data_buffers has waited - * for IO to complete before we evict it from the cache */ - filter_clear_page_cache(inode, iobuf); - rc = fsfilt_send_bio(rw, obd, inode, iobuf); CDEBUG(D_INFO, "tried to write %d pages, rc = %d\n", @@ -343,7 +358,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount, int rc) { struct obd_device *obd = exp->exp_obd; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct niobuf_local *lnb; struct fsfilt_objinfo fso; struct iattr iattr = { 0 }; @@ -390,7 +405,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount, iattr.ia_size = this_size; } - push_ctxt(&saved, &obd->obd_ctxt, NULL); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); cleanup_phase = 2; down(&inode->i_sem); @@ -417,10 +432,14 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int objcount, fsfilt_check_slow(now, obd_timeout, "direct_io"); err = fsfilt_commit_wait(obd, inode, wait_handle); - if (err) + if (err) { + CERROR("Failure to commit OST transaction (%d)?\n", err); rc = err; + } if (obd_sync_filter && !err) - LASSERT(oti->oti_transno <= obd->obd_last_committed); + LASSERTF(oti->oti_transno <= obd->obd_last_committed, + "oti_transno "LPU64" last_committed "LPU64"\n", + oti->oti_transno, obd->obd_last_committed); fsfilt_check_slow(now, obd_timeout, "commitrw commit"); cleanup: @@ -428,7 +447,7 @@ cleanup: switch (cleanup_phase) { case 2: - pop_ctxt(&saved, &obd->obd_ctxt, NULL); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); LASSERT(current->journal_info == NULL); case 1: filter_free_iobuf(iobuf); diff --git a/lustre/obdfilter/filter_io_26.c b/lustre/obdfilter/filter_io_26.c index a248361..a706583 100644 --- a/lustre/obdfilter/filter_io_26.c +++ b/lustre/obdfilter/filter_io_26.c @@ -123,11 +123,11 @@ static int dio_complete_routine(struct bio *bio, unsigned int done, int error) CERROR("***** bio->bi_private is NULL! This should never " "happen. Normally, I would crash here, but instead I " "will dump the bio contents to the console. Please " - "report this to CFS, along with any interesting messages " - "leading up to this point (like SCSI errors, perhaps). " - "Because bi_private is NULL, I can't wake up the thread " - "that initiated this I/O -- so you will probably have to " - "reboot this node."); + "report this to CFS, along with any interesting " + "messages leading up to this point (like SCSI errors, " + "perhaps). Because bi_private is NULL, I can't wake up " + "the thread that initiated this I/O -- so you will " + "probably have to reboot this node.\n"); CERROR("bi_next: %p, bi_flags: %lx, bi_rw: %lu, bi_vcnt: %d, " "bi_idx: %d, bi->size: %d, bi_end_io: %p, bi_cnt: %d, " "bi_private: %p\n", bio->bi_next, bio->bi_flags, @@ -343,7 +343,7 @@ int filter_do_bio(struct obd_device *obd, struct inode *inode, record_finish_io(dreq, rw, rc); } } - + out: wait_event(dreq->dr_wait, atomic_read(&dreq->dr_numreqs) == 0); @@ -352,89 +352,55 @@ int filter_do_bio(struct obd_device *obd, struct inode *inode, RETURN(rc); } -static void filter_clear_page_cache(struct inode *inode, struct dio_request *iobuf) +/* These are our hacks to keep our directio/bh IO coherent with ext3's + * page cache use. Most notably ext3 reads file data into the page + * cache when it is zeroing the tail of partial-block truncates and + * leaves it there, sometimes generating io from it at later truncates. + * This removes the partial page and its buffers from the page cache, + * so it should only ever cause a wait in rare cases, as otherwise we + * always do full-page IO to the OST. + * + * The call to truncate_complete_page() will call journal_invalidatepage() + * to free the buffers and drop the page from cache. The buffers should + * not be dirty, because we already called fdatasync/fdatawait on them. + */ +static int filter_clear_page_cache(struct inode *inode, + struct dio_request *iobuf) { struct page *page; - int i; + int i, rc, rc2; + + /* This is nearly generic_osync_inode, without the waiting on the inode + rc = generic_osync_inode(inode, inode->i_mapping, + OSYNC_DATA|OSYNC_METADATA); + */ + rc = filemap_fdatawrite(inode->i_mapping); + rc2 = sync_mapping_buffers(inode->i_mapping); + if (rc == 0) + rc = rc2; + rc2 = filemap_fdatawait(inode->i_mapping); + if (rc == 0) + rc = rc2; + if (rc != 0) + RETURN(rc); - for (i = 0; i < iobuf->dr_npages ; i++) { + /* be careful to call this after fsync_inode_data_buffers has waited + * for IO to complete before we evict it from the cache */ + for (i = 0; i < iobuf->dr_npages; i++) { page = find_lock_page(inode->i_mapping, iobuf->dr_pages[i]->index); if (page == NULL) continue; if (page->mapping != NULL) { - block_invalidatepage(page, 0); + wait_on_page_writeback(page); ll_truncate_complete_page(page); } unlock_page(page); page_cache_release(page); } -} - -static int filter_quota_enforcement(struct obd_device *obd, - unsigned int fsuid, unsigned int fsgid, - struct obd_ucred **ret_uc) -{ - struct filter_obd *filter = &obd->u.filter; - struct obd_ucred *uc = NULL; - ENTRY; - - if (!sb_any_quota_enabled(filter->fo_sb)) - RETURN(0); - OBD_ALLOC(uc, sizeof(*uc)); - if (!uc) - RETURN(-ENOMEM); - *ret_uc = uc; - - uc->ouc_fsuid = fsuid; - uc->ouc_fsgid = fsgid; - uc->ouc_cap = current->cap_effective; - if (!fsuid) - cap_raise(uc->ouc_cap, CAP_SYS_RESOURCE); - else - cap_lower(uc->ouc_cap, CAP_SYS_RESOURCE); - - RETURN(0); -} - -static int filter_get_quota_flag(struct obd_device *obd, - struct obdo *oa) -{ - struct filter_obd *filter = &obd->u.filter; - int cnt; - int rc = 0, err; - ENTRY; - - if (!sb_any_quota_enabled(filter->fo_sb)) - RETURN(rc); - - oa->o_flags = QUOTA_OK; - - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - struct obd_quotactl oqctl; - - oqctl.qc_cmd = Q_GETQUOTA; - oqctl.qc_type = cnt; - oqctl.qc_id = (cnt == USRQUOTA) ? oa->o_uid : oa->o_gid; - err = fsfilt_quotactl(obd, filter->fo_sb, &oqctl); - if (err) { - if (!rc) - rc = err; - continue; - } - - /* set over quota flags for a uid/gid */ - oa->o_valid |= (cnt == USRQUOTA) ? - OBD_MD_FLUSRQUOTA : OBD_MD_FLGRPQUOTA; - if (oqctl.qc_dqblk.dqb_bhardlimit && - (toqb(oqctl.qc_dqblk.dqb_curspace) > oqctl.qc_dqblk.dqb_bhardlimit)) - oa->o_flags |= (cnt == USRQUOTA) ? - OBD_FL_NO_USRQUOTA : OBD_FL_NO_GRPQUOTA; - } - - RETURN(rc); + return 0; } /* Must be called with i_sem taken for writes; this will drop it */ @@ -446,7 +412,6 @@ int filter_direct_io(int rw, struct dentry *dchild, void *iobuf, struct dio_request *dreq = iobuf; struct inode *inode = dchild->d_inode; int blocks_per_page = PAGE_SIZE >> inode->i_blkbits; - struct lustre_quota_ctxt *qctxt = &obd->u.filter.fo_quota_ctxt; int rc, rc2; ENTRY; @@ -465,7 +430,7 @@ int filter_direct_io(int rw, struct dentry *dchild, void *iobuf, cap_raise(current->cap_effective, CAP_SYS_RESOURCE); dreq->dr_flag &= ~OBD_BRW_FROM_GRANT; } - + remap: rc = fsfilt_map_inode_pages(obd, inode, dreq->dr_pages, dreq->dr_npages, @@ -474,18 +439,17 @@ remap: rw == OBD_BRW_WRITE, NULL); if (rc == -EDQUOT) { - LASSERT(rw == OBD_BRW_WRITE && + LASSERT(rw == OBD_BRW_WRITE && !cap_raised(current->cap_effective, CAP_SYS_RESOURCE)); - /* Unfortunately, if quota master is too busy to handle the - * pre-dqacq in time or this user has exceeded quota limit, we - * have to wait for the completion of in flight dqacq/dqrel, + /* Unfortunately, if quota master is too busy to handle the + * pre-dqacq in time or this user has exceeded quota limit, we + * have to wait for the completion of in flight dqacq/dqrel, * then try again */ - if (qctxt_wait_on_dqacq(obd, qctxt, inode->i_uid, - inode->i_gid, 1) == -EAGAIN) + if (filter_quota_check_master(obd, inode)) goto remap; } - + if (rw == OBD_BRW_WRITE) { if (rc == 0) { filter_tally_write(&obd->u.filter, @@ -511,24 +475,10 @@ remap: RETURN(rc); } - /* This is nearly osync_inode, without the waiting - rc = generic_osync_inode(inode, inode->i_mapping, - OSYNC_DATA|OSYNC_METADATA); */ - rc = filemap_fdatawrite(inode->i_mapping); - rc2 = sync_mapping_buffers(inode->i_mapping); - if (rc == 0) - rc = rc2; - rc2 = filemap_fdatawait(inode->i_mapping); - if (rc == 0) - rc = rc2; - + rc = filter_clear_page_cache(inode, dreq); if (rc != 0) RETURN(rc); - /* be careful to call this after fsync_inode_data_buffers has waited - * for IO to complete before we evict it from the cache */ - filter_clear_page_cache(inode, dreq); - RETURN(filter_do_bio(obd, inode, dreq, rw)); } @@ -560,7 +510,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, { struct niobuf_local *lnb; struct dio_request *dreq = NULL; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct fsfilt_objinfo fso; struct iattr iattr = { 0 }; struct inode *inode = NULL; @@ -568,7 +518,7 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, int i, err, cleanup_phase = 0; struct obd_device *obd = exp->exp_obd; struct filter_obd *filter = &obd->u.filter; - struct obd_ucred *uc = NULL; + struct lvfs_ucred *uc = NULL; int total_size = 0; ENTRY; @@ -620,12 +570,12 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, /* The client store the user credit information fsuid and fsgid * in oa->o_uid and oa->o_gid. In case of quota enabled, we use - * them to build the obd_ucred so as to enforce oss quota check */ + * them to build the lvfs_ucred so as to enforce oss quota check */ rc = filter_quota_enforcement(obd, oa->o_uid, oa->o_gid, &uc); if (rc) GOTO(cleanup, rc); - push_ctxt(&saved, &obd->obd_ctxt, uc); + push_ctxt(&saved, &obd->obd_lvfs_ctxt, uc); cleanup_phase = 2; down(&inode->i_sem); @@ -662,7 +612,9 @@ int filter_commitrw_write(struct obd_export *exp, struct obdo *oa, rc = err; if (obd_sync_filter && !err) - LASSERT(oti->oti_transno <= obd->obd_last_committed); + LASSERTF(oti->oti_transno <= obd->obd_last_committed, + "oti_transno "LPU64" last_committed "LPU64"\n", + oti->oti_transno, obd->obd_last_committed); fsfilt_check_slow(now, obd_timeout, "commitrw commit"); @@ -671,7 +623,7 @@ cleanup: switch (cleanup_phase) { case 2: - pop_ctxt(&saved, &obd->obd_ctxt, uc); + pop_ctxt(&saved, &obd->obd_lvfs_ctxt, uc); if (uc) OBD_FREE(uc, sizeof(*uc)); LASSERT(current->journal_info == NULL); diff --git a/lustre/obdfilter/filter_log.c b/lustre/obdfilter/filter_log.c index 34ad008..2ddfe72 100644 --- a/lustre/obdfilter/filter_log.c +++ b/lustre/obdfilter/filter_log.c @@ -137,7 +137,7 @@ static int filter_recov_log_unlink_cb(struct llog_ctxt *ctxt, memcpy(obdo_logcookie(oa), cookie, sizeof(*cookie)); oid = oa->o_id; - rc = obd_destroy(exp, oa, NULL, NULL); + rc = filter_destroy(exp, oa, NULL, NULL); obdo_free(oa); if (rc == -ENOENT) { CDEBUG(D_HA, "object already removed, send cookie\n"); @@ -165,10 +165,10 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt, obd_id oid; int rc = 0; ENTRY; - + lsr = (struct llog_setattr_rec *)rec; oa = obdo_alloc(); - + oa->o_valid |= (OBD_MD_FLID | OBD_MD_FLUID | OBD_MD_FLGID | OBD_MD_FLCOOKIE); oa->o_id = lsr->lsr_oid; @@ -178,7 +178,7 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt, memcpy(obdo_logcookie(oa), cookie, sizeof(*cookie)); oid = oa->o_id; - rc = obd_setattr(exp, oa, NULL, NULL); + rc = filter_setattr(exp, oa, NULL, NULL); obdo_free(oa); if (rc == -ENOENT) { @@ -186,10 +186,10 @@ static int filter_recov_log_setattr_cb(struct llog_ctxt *ctxt, llog_cancel(ctxt, NULL, 1, cookie, 0); RETURN(0); } - + if (rc == 0) CDEBUG(D_HA, "object: "LPU64" in record is chown/chgrp\n", oid); - + RETURN(rc); } diff --git a/lustre/obdfilter/filter_lvb.c b/lustre/obdfilter/filter_lvb.c index c8a9d9b..e0ff9eb 100644 --- a/lustre/obdfilter/filter_lvb.c +++ b/lustre/obdfilter/filter_lvb.c @@ -100,7 +100,7 @@ static int filter_lvbo_update(struct ldlm_resource *res, struct lustre_msg *m, int buf_idx, int increase) { int rc = 0; - struct ost_lvb *lvb = res->lr_lvb_data; + struct ost_lvb *lvb; struct obd_device *obd; struct dentry *dentry; ENTRY; @@ -113,6 +113,7 @@ static int filter_lvbo_update(struct ldlm_resource *res, struct lustre_msg *m, RETURN(0); down(&res->lr_lvb_sem); + lvb = res->lr_lvb_data; if (lvb == NULL) { CERROR("No lvb when running lvbo_update!\n"); GOTO(out, rc = 0); diff --git a/lustre/obdfilter/filter_san.c b/lustre/obdfilter/filter_san.c index 4b1e14d..0340d6b 100644 --- a/lustre/obdfilter/filter_san.c +++ b/lustre/obdfilter/filter_san.c @@ -38,20 +38,29 @@ 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, diff --git a/lustre/obdfilter/lproc_obdfilter.c b/lustre/obdfilter/lproc_obdfilter.c index 61d0fb8..ab7e4c5 100644 --- a/lustre/obdfilter/lproc_obdfilter.c +++ b/lustre/obdfilter/lproc_obdfilter.c @@ -29,11 +29,7 @@ #include "filter_internal.h" -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -#else - +#ifdef LPROCFS static int lprocfs_filter_rd_groups(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -121,121 +117,6 @@ int lprocfs_filter_wr_readcache(struct file *file, const char *buffer, return count; } -static int lprocfs_filter_rd_bunit(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.filter.fo_quota_ctxt.lqc_bunit_sz); -} - -static int lprocfs_filter_rd_iunit(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.filter.fo_quota_ctxt.lqc_iunit_sz); -} - -static int lprocfs_filter_wr_bunit(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val % QUOTABLOCK_SIZE || - val <= obd->u.filter.fo_quota_ctxt.lqc_btune_sz) - return -EINVAL; - - obd->u.filter.fo_quota_ctxt.lqc_bunit_sz = val; - return count; -} - -static int lprocfs_filter_wr_iunit(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= obd->u.filter.fo_quota_ctxt.lqc_itune_sz) - return -EINVAL; - - obd->u.filter.fo_quota_ctxt.lqc_iunit_sz = val; - return count; -} - -static int lprocfs_filter_rd_btune(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.filter.fo_quota_ctxt.lqc_btune_sz); -} - -static int lprocfs_filter_rd_itune(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - LASSERT(obd != NULL); - - return snprintf(page, count, "%lu\n", - obd->u.filter.fo_quota_ctxt.lqc_itune_sz); -} - -static int lprocfs_filter_wr_btune(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= QUOTABLOCK_SIZE * MIN_QLIMIT || val % QUOTABLOCK_SIZE || - val >= obd->u.filter.fo_quota_ctxt.lqc_bunit_sz) - return -EINVAL; - - obd->u.filter.fo_quota_ctxt.lqc_btune_sz = val; - return count; -} - -static int lprocfs_filter_wr_itune(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - struct obd_device *obd = (struct obd_device *)data; - int val, rc = 0; - LASSERT(obd != NULL); - - rc = lprocfs_write_helper(buffer, count, &val); - if (rc) - return rc; - - if (val <= MIN_QLIMIT || - val >= obd->u.filter.fo_quota_ctxt.lqc_iunit_sz) - return -EINVAL; - - obd->u.filter.fo_quota_ctxt.lqc_itune_sz = val; - return count; -} - static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { "blocksize", lprocfs_rd_blksize, 0, 0 }, @@ -257,14 +138,16 @@ static struct lprocfs_vars lprocfs_obd_vars[] = { { "readcache_max_filesize", lprocfs_filter_rd_readcache, lprocfs_filter_wr_readcache, 0 }, - { "quota_bunit_sz", lprocfs_filter_rd_bunit, +#ifdef HAVE_QUOTA_SUPPORT + { "quota_bunit_sz", lprocfs_filter_rd_bunit, lprocfs_filter_wr_bunit, 0}, { "quota_btune_sz", lprocfs_filter_rd_btune, lprocfs_filter_wr_btune, 0}, - { "quota_iunit_sz", lprocfs_filter_rd_iunit, + { "quota_iunit_sz", lprocfs_filter_rd_iunit, lprocfs_filter_wr_iunit, 0}, { "quota_itune_sz", lprocfs_filter_rd_itune, lprocfs_filter_wr_itune, 0}, +#endif { 0 } }; @@ -369,7 +252,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) unsigned long w = filter->fo_w_pages.oh_buckets[i]; read_cum += r; write_cum += w; - seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", + seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", 1 << i, r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), @@ -393,7 +276,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) unsigned long w = filter->fo_w_discont_pages.oh_buckets[i]; read_cum += r; write_cum += w; - seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", + seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", i, r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), @@ -416,7 +299,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) unsigned long w = filter->fo_w_discont_blocks.oh_buckets[i]; read_cum += r; write_cum += w; - seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", + seq_printf(seq, "%u:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", i, r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), @@ -440,7 +323,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) unsigned long w = filter->fo_write_rpc_hist.oh_buckets[i]; read_cum += r; write_cum += w; - seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", + seq_printf(seq, "%u:\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", i, r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), @@ -463,7 +346,7 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) unsigned long w = filter->fo_w_io_time.oh_buckets[i]; read_cum += r; write_cum += w; - seq_printf(seq, "%d:\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", + seq_printf(seq, "%10u:\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", 1 << i, r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), @@ -487,15 +370,18 @@ static int filter_brw_stats_seq_show(struct seq_file *seq, void *v) read_cum += r; write_cum += w; + if (read_cum == 0 && write_cum == 0) + continue; + if (i < 10) - seq_printf(seq, "%d", 1<<i); + seq_printf(seq, "%u", 1<<i); else if (i < 20) - seq_printf(seq, "%dK", 1<<(i-10)); + seq_printf(seq, "%uK", 1<<(i-10)); else - seq_printf(seq, "%dM", 1<<(i-20)); + seq_printf(seq, "%uM", 1<<(i-20)); seq_printf(seq, ":\t\t%10lu %3lu %3lu | %10lu %3lu %3lu\n", - r, pct(r, read_tot), pct(read_cum, read_tot), + r, pct(r, read_tot), pct(read_cum, read_tot), w, pct(w, write_tot), pct(write_cum, write_tot)); if (read_cum == read_tot && write_cum == write_tot) break; @@ -579,7 +465,5 @@ int lproc_filter_attach_seqstat(struct obd_device *dev) &filter_brw_stats_fops, dev); } - - -#endif /* LPROCFS */ LPROCFS_INIT_VARS(filter, lprocfs_module_vars, lprocfs_obd_vars) +#endif /* LPROCFS */ diff --git a/lustre/osc/Makefile.in b/lustre/osc/Makefile.in index 52fece6..568a725 100644 --- a/lustre/osc/Makefile.in +++ b/lustre/osc/Makefile.in @@ -1,4 +1,4 @@ MODULES := osc -osc-objs := osc_request.o lproc_osc.o osc_lib.o osc_create.o osc_quota.o +osc-objs := osc_request.o lproc_osc.o osc_lib.o osc_create.o @INCLUDE_RULES@ diff --git a/lustre/osc/autoMakefile.am b/lustre/osc/autoMakefile.am index dae803a..af0649d 100644 --- a/lustre/osc/autoMakefile.am +++ b/lustre/osc/autoMakefile.am @@ -14,5 +14,5 @@ if MODULES modulefs_DATA = osc$(KMODEXT) endif -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ DIST_SOURCES = $(osc-objs:%.o=%.c) osc_internal.h diff --git a/lustre/osc/lproc_osc.c b/lustre/osc/lproc_osc.c index e02603b..3127e0b 100644 --- a/lustre/osc/lproc_osc.c +++ b/lustre/osc/lproc_osc.c @@ -30,13 +30,9 @@ #include <linux/seq_file.h> #include "osc_internal.h" -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -#else - -int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off, int count, - int *eof, void *data) +#ifdef LPROCFS +static int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -48,8 +44,8 @@ int osc_rd_max_pages_per_rpc(char *page, char **start, off_t off, int count, return rc; } -int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer, - unsigned long count, void *data) +static int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer, + unsigned long count, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -69,8 +65,8 @@ int osc_wr_max_pages_per_rpc(struct file *file, const char *buffer, return count; } -int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -82,8 +78,8 @@ int osc_rd_max_rpcs_in_flight(char *page, char **start, off_t off, int count, return rc; } -int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer, - unsigned long count, void *data) +static int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer, + unsigned long count, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -103,8 +99,8 @@ int osc_wr_max_rpcs_in_flight(struct file *file, const char *buffer, return count; } -int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count, + int *eof, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -117,8 +113,8 @@ int osc_rd_max_dirty_mb(char *page, char **start, off_t off, int count, return snprintf(page, count, "%u\n", val); } -int osc_wr_max_dirty_mb(struct file *file, const char *buffer, - unsigned long count, void *data) +static int osc_wr_max_dirty_mb(struct file *file, const char *buffer, + unsigned long count, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -139,8 +135,8 @@ int osc_wr_max_dirty_mb(struct file *file, const char *buffer, return count; } -int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -152,8 +148,8 @@ int osc_rd_cur_dirty_bytes(char *page, char **start, off_t off, int count, return rc; } -int osc_rd_cur_grant_bytes(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_cur_grant_bytes(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *dev = data; struct client_obd *cli = &dev->u.cli; @@ -165,8 +161,8 @@ int osc_rd_cur_grant_bytes(char *page, char **start, off_t off, int count, return rc; } -int osc_rd_create_count(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_create_count(char *page, char **start, off_t off, int count, + int *eof, void *data) { struct obd_device *obd = data; @@ -177,8 +173,8 @@ int osc_rd_create_count(char *page, char **start, off_t off, int count, obd->u.cli.cl_oscc.oscc_grow_count); } -int osc_wr_create_count(struct file *file, const char *buffer, - unsigned long count, void *data) +static int osc_wr_create_count(struct file *file, const char *buffer, + unsigned long count, void *data) { struct obd_device *obd = data; int val, rc; @@ -200,8 +196,8 @@ int osc_wr_create_count(struct file *file, const char *buffer, return count; } -int osc_rd_prealloc_next_id(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_prealloc_next_id(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *obd = data; @@ -212,8 +208,8 @@ int osc_rd_prealloc_next_id(char *page, char **start, off_t off, int count, obd->u.cli.cl_oscc.oscc_next_id); } -int osc_rd_prealloc_last_id(char *page, char **start, off_t off, int count, - int *eof, void *data) +static int osc_rd_prealloc_last_id(char *page, char **start, off_t off, + int count, int *eof, void *data) { struct obd_device *obd = data; @@ -224,6 +220,36 @@ int osc_rd_prealloc_last_id(char *page, char **start, off_t off, int count, obd->u.cli.cl_oscc.oscc_last_id); } +static int osc_rd_checksum(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct obd_device *obd = data; + + if (obd == NULL) + return 0; + + return snprintf(page, count, "%d\n", + obd->u.cli.cl_checksum ? 1 : 0); +} + +static int osc_wr_checksum(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + struct obd_device *obd = data; + int val, rc; + + if (obd == NULL) + return 0; + + rc = lprocfs_write_helper(buffer, count, &val); + if (rc) + return rc; + + obd->u.cli.cl_checksum = (val ? 1 : 0); + + return count; +} + static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { "ping", 0, lprocfs_wr_ping, 0 }, @@ -246,6 +272,7 @@ static struct lprocfs_vars lprocfs_obd_vars[] = { { "create_count", osc_rd_create_count, osc_wr_create_count, 0 }, { "prealloc_next_id", osc_rd_prealloc_next_id, 0, 0 }, { "prealloc_last_id", osc_rd_prealloc_last_id, 0, 0 }, + { "checksums", osc_rd_checksum, osc_wr_checksum, 0 }, { 0 } }; @@ -421,5 +448,5 @@ int lproc_osc_attach_seqstat(struct obd_device *dev) &osc_rpc_stats_fops, dev); } -#endif /* LPROCFS */ LPROCFS_INIT_VARS(osc, lprocfs_module_vars, lprocfs_obd_vars) +#endif /* LPROCFS */ diff --git a/lustre/osc/osc_create.c b/lustre/osc/osc_create.c index ededb7e..27d1e41 100644 --- a/lustre/osc/osc_create.c +++ b/lustre/osc/osc_create.c @@ -70,6 +70,8 @@ static int osc_interpret_create(struct ptlrpc_request *req, void *data, int rc) } oscc = req->rq_async_args.pointer_arg[0]; + LASSERT(oscc && (oscc->oscc_obd != LP_POISON)); + spin_lock(&oscc->oscc_lock); oscc->oscc_flags &= ~OSCC_FLAG_CREATING; if (rc == -ENOSPC || rc == -EROFS) { diff --git a/lustre/osc/osc_internal.h b/lustre/osc/osc_internal.h index 8fe19d9..60e9160 100644 --- a/lustre/osc/osc_internal.h +++ b/lustre/osc/osc_internal.h @@ -32,6 +32,10 @@ struct osc_async_page { void *oap_caller_data; }; +#define OAP_FROM_COOKIE(c) \ + (LASSERT(((struct osc_async_page *)(c))->oap_magic == OAP_MAGIC), \ + (struct osc_async_page *)(c)) + struct osc_cache_waiter { struct list_head ocw_entry; wait_queue_head_t ocw_waitq; @@ -52,6 +56,8 @@ int osc_real_create(struct obd_export *exp, struct obdo *oa, struct lov_stripe_md **ea, struct obd_trans_info *oti); void oscc_init(struct obd_device *obd); void osc_wake_cache_waiters(struct client_obd *cli); + +#ifdef HAVE_QUOTA_SUPPORT int osc_get_quota_flag(struct client_obd *cli, unsigned int uid, unsigned int gid); int osc_set_quota_flag(struct client_obd *cli, @@ -60,8 +66,45 @@ int osc_set_quota_flag(struct client_obd *cli, int osc_qinfo_cleanup(struct client_obd *cli); int osc_qinfo_init(void); void osc_qinfo_exit(void); +int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl); +int osc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk); +int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl); +#else +static inline int osc_get_quota_flag(struct client_obd *cli, + unsigned int uid, unsigned int gid) +{ + return QUOTA_OK; +} +static inline int osc_set_quota_flag(struct client_obd *cli, + unsigned int uid, unsigned int gid, + obd_flag valid, obd_flag flags) +{ + return 0; +} +static inline int osc_qinfo_cleanup(struct client_obd *cli) +{ + return 0; +} +static inline int osc_qinfo_init(void) +{ + return 0; +} +static inline void osc_qinfo_exit(void) {} +static inline int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} +static inline int osc_poll_quotacheck(struct obd_export *exp, struct if_quotacheck *qchk) +{ + return -ENOTSUPP; +} +static inline int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + return -ENOTSUPP; +} +#endif -#ifdef __KERNEL__ +#ifdef LPROCFS int lproc_osc_attach_seqstat(struct obd_device *dev); #else static inline int lproc_osc_attach_seqstat(struct obd_device *dev) {return 0;} diff --git a/lustre/osc/osc_quota.c b/lustre/osc/osc_quota.c index b09c2c5..60446b2 100644 --- a/lustre/osc/osc_quota.c +++ b/lustre/osc/osc_quota.c @@ -3,20 +3,8 @@ * * Copyright (c) 2003 Cluster File Systems, Inc. * - * This file is part of Lustre, http://www.lustre.org. + * No redistribution or use is permitted outside of Cluster File Systems, Inc. * - * Lustre is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * Lustre is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Lustre; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef EXPORT_SYMTAB @@ -26,7 +14,6 @@ #ifdef __KERNEL__ # include <linux/module.h> -# include <linux/obd.h> # include <linux/obd_ost.h> # include <linux/lustre_net.h> # include <linux/lustre_dlm.h> @@ -38,8 +25,11 @@ # else # include <linux/locks.h> # endif +#else +# include <liblustre.h> #endif +#include <linux/obd.h> #include "osc_internal.h" struct osc_quota_info { @@ -100,20 +90,19 @@ static struct osc_quota_info *alloc_qinfo(struct client_obd *cli, { struct osc_quota_info *oqi; ENTRY; - - OBD_SLAB_ALLOC(oqi, qinfo_cachep, SLAB_KERNEL, - sizeof(*oqi)); + + OBD_SLAB_ALLOC(oqi, qinfo_cachep, SLAB_KERNEL, sizeof(*oqi)); if(!oqi) RETURN(NULL); - + INIT_LIST_HEAD(&oqi->oqi_hash); oqi->oqi_cli = cli; oqi->oqi_id = id; oqi->oqi_type = type; - + RETURN(oqi); } - + static void free_qinfo(struct osc_quota_info *oqi) { OBD_SLAB_FREE(oqi, qinfo_cachep, sizeof(*oqi)); @@ -242,3 +231,96 @@ void osc_qinfo_exit(void) LASSERTF(kmem_cache_destroy(qinfo_cachep) == 0, "couldn't destroy osc quota info slab\n"); } + +int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + struct client_obd *cli = &exp->exp_obd->u.cli; + struct ptlrpc_request *req; + struct obd_quotactl *body; + int size = sizeof(*body); + int rc; + ENTRY; + + req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACHECK, 1, &size, + NULL); + if (!req) + GOTO(out, rc = -ENOMEM); + + body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body)); + memcpy(body, oqctl, sizeof(*body)); + + req->rq_replen = lustre_msg_size(0, NULL); + + spin_lock(&cli->cl_qchk_lock); + cli->cl_qchk_stat = CL_QUOTACHECKING; + spin_unlock(&cli->cl_qchk_lock); + + rc = ptlrpc_queue_wait(req); + if (rc) { + spin_lock(&cli->cl_qchk_lock); + cli->cl_qchk_stat = rc; + spin_unlock(&cli->cl_qchk_lock); + } + out: + ptlrpc_req_finished(req); + RETURN (rc); +} + +int osc_poll_quotacheck(struct obd_export *exp, + struct if_quotacheck *qchk) +{ + struct client_obd *cli = &exp->exp_obd->u.cli; + int stat; + ENTRY; + + spin_lock(&cli->cl_qchk_lock); + stat = cli->cl_qchk_stat; + spin_unlock(&cli->cl_qchk_lock); + + qchk->stat = stat; + if (stat == CL_QUOTACHECKING) { + qchk->stat = -ENODATA; + stat = 0; + } else if (qchk->stat) { + if (qchk->stat > CL_QUOTACHECKING) + qchk->stat = stat = -EINTR; + + strncpy(qchk->obd_type, "obdfilter", 10); + qchk->obd_uuid = cli->cl_import->imp_target_uuid; + } + RETURN(stat); +} + +int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) +{ + struct ptlrpc_request *req; + struct obd_quotactl *oqc; + int size = sizeof(*oqctl); + int rc; + ENTRY; + + req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACTL, 1, &size, + NULL); + if (!req) + GOTO(out, rc = -ENOMEM); + + memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size); + + req->rq_replen = lustre_msg_size(1, &size); + + rc = ptlrpc_queue_wait(req); + if (!rc) { + oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc), + lustre_swab_obd_quotactl); + if (oqc == NULL) { + CERROR ("Can't unpack mds_body\n"); + GOTO(out, rc = -EPROTO); + } + + memcpy(oqctl, oqc, sizeof(*oqctl)); + } +out: + ptlrpc_req_finished(req); + RETURN (rc); +} + diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c index 3cfb2be..c4687b4 100644 --- a/lustre/osc/osc_request.c +++ b/lustre/osc/osc_request.c @@ -62,6 +62,7 @@ #include <linux/lustre_ha.h> #include <linux/lprocfs_status.h> #include <linux/lustre_log.h> +#include <linux/lustre_debug.h> #include "osc_internal.h" /* Pack OSC object metadata for disk storage (LE byte order). */ @@ -301,9 +302,9 @@ static int osc_setattr_async(struct obd_export *exp, struct obdo *oa, memcpy(&body->oa, oa, sizeof(*oa)); request->rq_replen = lustre_msg_size(1, &size); - /* do mds to ost setattr asynchronouly */ + /* do mds to ost setattr asynchronouly */ ptlrpcd_add_req(request); - + RETURN(rc); } @@ -716,37 +717,29 @@ static inline int can_merge_pages(struct brw_page *p1, struct brw_page *p2) return (p1->off + p1->count == p2->off); } -#if CHECKSUM_BULK -static obd_count cksum_blocks(int nob, obd_count page_count, - struct brw_page *pga) +static obd_count osc_checksum_bulk(int nob, obd_count pg_count, + struct brw_page *pga) { - obd_count cksum = 0; + __u32 cksum = ~0; - LASSERT (page_count > 0); - while (nob > 0) { + LASSERT (pg_count > 0); + while (nob > 0 && pg_count > 0) { char *ptr = kmap(pga->pg); - int psum, off = pga->off & ~PAGE_MASK; + int off = pga->off & ~PAGE_MASK; int count = pga->count > nob ? nob : pga->count; - while (count > 0) { - ost_checksum(&cksum, &psum, ptr + off, - count > CHECKSUM_CHUNK ? - CHECKSUM_CHUNK : count); - LL_CDEBUG_PAGE(D_PAGE, pga->pg, "off %d checksum %x\n", - off, psum); - off += CHECKSUM_CHUNK; - count -= CHECKSUM_CHUNK; - } + cksum = crc32_le(cksum, ptr + off, count); kunmap(pga->pg); + LL_CDEBUG_PAGE(D_PAGE, pga->pg, "off %d checksum %x\n", + off, cksum); nob -= pga->count; - page_count--; + pg_count--; pga++; } - return (cksum); + return cksum; } -#endif static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa, struct lov_stripe_md *lsm, obd_count page_count, @@ -837,14 +830,22 @@ static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa, /* size[0] still sizeof (*body) */ if (opc == OST_WRITE) { -#if CHECKSUM_BULK - body->oa.o_valid |= OBD_MD_FLCKSUM; - body->oa.o_cksum = cksum_pages(requested_nob, page_count, pga); -#endif + if (unlikely(cli->cl_checksum)) { + body->oa.o_valid |= OBD_MD_FLCKSUM; + body->oa.o_cksum = osc_checksum_bulk(requested_nob, + page_count, pga); + CDEBUG(D_PAGE, "checksum at write origin: %x\n", + body->oa.o_cksum); + /* save this in 'oa', too, for later checking */ + oa->o_valid |= OBD_MD_FLCKSUM; + oa->o_cksum = body->oa.o_cksum; + } /* 1 RC per niobuf */ size[1] = sizeof(__u32) * niocount; req->rq_replen = lustre_msg_size(2, size); } else { + if (unlikely(cli->cl_checksum)) + body->oa.o_valid |= OBD_MD_FLCKSUM; /* 1 RC for the whole I/O */ req->rq_replen = lustre_msg_size(1, size); } @@ -859,13 +860,49 @@ static int osc_brw_prep_request(int cmd, struct obd_import *imp,struct obdo *oa, return (rc); } +static void check_write_csum(__u32 cli, __u32 srv, int requested_nob, + obd_count page_count, struct brw_page *pga) +{ + __u32 new_csum; + + if (srv == cli) { + CDEBUG(D_PAGE, "checksum %x confirmed\n", cli); + return; + } + + new_csum = osc_checksum_bulk(requested_nob, page_count, pga); + + if (new_csum == srv) { + CERROR("BAD CHECKSUM (WRITE): pages were mutated on the client" + "after we checksummed them (original client csum:" + " %x; server csum: %x; client csum now: %x)\n", + cli, srv, new_csum); + return; + } + + if (new_csum == cli) { + CERROR("BAD CHECKSUM (WRITE): pages were mutated in transit " + "(original client csum: %x; server csum: %x; client " + "csum now: %x)\n", cli, srv, new_csum); + return; + } + + CERROR("BAD CHECKSUM (WRITE): pages were mutated in transit, and the " + "current page contents don't match the originals OR what the " + "server received (original client csum: %x; server csum: %x; " + "client csum now: %x)\n", cli, srv, new_csum); +} + static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa, int requested_nob, int niocount, obd_count page_count, struct brw_page *pga, int rc) { + const ptl_process_id_t *peer = + &req->rq_import->imp_connection->c_peer; struct client_obd *cli = &req->rq_import->imp_obd->u.cli; struct ost_body *body; + __u32 client_cksum = 0; ENTRY; if (rc < 0 && rc != -EDQUOT) @@ -886,6 +923,9 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa, if (rc < 0) RETURN(rc); + if (unlikely(oa->o_valid & OBD_MD_FLCKSUM)) + client_cksum = oa->o_cksum; /* save for later */ + osc_update_grant(cli, body); memcpy(oa, &body->oa, sizeof(*oa)); @@ -896,10 +936,17 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa, } LASSERT (req->rq_bulk->bd_nob == requested_nob); + if (unlikely((oa->o_valid & OBD_MD_FLCKSUM) && + client_cksum)) { + check_write_csum(client_cksum, oa->o_cksum, + requested_nob, page_count, pga); + } + RETURN(check_write_rcs(req, requested_nob, niocount, page_count, pga)); } + /* The rest of this function executes only for OST_READs */ if (rc > requested_nob) { CERROR("Unexpected rc %d (%d requested)\n", rc, requested_nob); RETURN(-EPROTO); @@ -914,37 +961,40 @@ static int osc_brw_fini_request(struct ptlrpc_request *req, struct obdo *oa, if (rc < requested_nob) handle_short_read(rc, page_count, pga); -#if CHECKSUM_BULK - if (oa->o_valid & OBD_MD_FLCKSUM) { - const ptl_process_id_t peer = - req->rq_import->imp_connection->c_peer; + if (unlikely(oa->o_valid & OBD_MD_FLCKSUM)) { static int cksum_counter; - obd_count server_cksum = oa->o_cksum; - obd_count cksum = cksum_pages(rc, page_count, pga); - char *str = libcfs_nid2str(peer.nid); + __u32 cksum = osc_checksum_bulk(rc, page_count, pga); + __u32 server_cksum = oa->o_cksum; + + if (server_cksum == ~0 && rc > 0) { + CERROR("Protocol error: server %s set the 'checksum' " + "bit, but didn't send a checksum. Not fatal, " + "but please tell CFS.\n", + libcfs_nid2str(peer->nid)); + RETURN(0); + } cksum_counter++; + if (server_cksum != cksum) { - CERROR("Bad checksum: server %x, client %x, server NID " - LPX64" (%s)\n", server_cksum, cksum, - peer.nid, str); + CERROR("Bad checksum from %s: server %x != client %x\n", + libcfs_nid2str(peer->nid), server_cksum, cksum); cksum_counter = 0; oa->o_cksum = cksum; } else if ((cksum_counter & (-cksum_counter)) == cksum_counter){ - CWARN("Checksum %u from "LPX64" (%s) OK: %x\n", - cksum_counter, peer.nid, str, cksum); + CWARN("Checksum %u from %s OK: %x\n", + cksum_counter, libcfs_nid2str(peer->nid), cksum); } - CDEBUG(D_PAGE, "checksum %x\n", cksum); - } else { + CDEBUG(D_PAGE, "checksum %x confirmed\n", cksum); + } else if (unlikely(client_cksum)) { static int cksum_missed; cksum_missed++; if ((cksum_missed & (-cksum_missed)) == cksum_missed) - CERROR("Request checksum %u from "LPX64", no reply\n", - cksum_missed, - req->rq_import->imp_connection->c_peer.nid); + CERROR("Checksum %u requested from %s but not sent\n", + cksum_missed, libcfs_nid2str(peer->nid)); } -#endif + RETURN(0); } @@ -1380,10 +1430,10 @@ static struct ptlrpc_request *osc_build_req(struct client_obd *cli, * at present. And we chose one page's user credit information as * the whole rpc's credit information. FIXME */ if (cmd == OBD_BRW_WRITE) { - struct obd_ucred ouc; + struct lvfs_ucred ouc; ops->ap_get_ucred(caller_data, &ouc); - oa->o_uid = ouc.ouc_fsuid; - oa->o_gid = ouc.ouc_fsgid; + oa->o_uid = ouc.luc_fsuid; + oa->o_gid = ouc.luc_fsgid; } sort_brw_pages(pga, page_count); @@ -1583,7 +1633,7 @@ static int osc_send_oap_rpc(struct client_obd *cli, struct lov_oinfo *loi, list_splice(&rpc_list, &aa->aa_oaps); INIT_LIST_HEAD(&rpc_list); -#ifdef __KERNEL__ +#ifdef LPROCFS if (cmd == OBD_BRW_READ) { lprocfs_oh_tally_log2(&cli->cl_read_page_hist, page_count); lprocfs_oh_tally(&cli->cl_read_rpc_hist, cli->cl_r_in_flight); @@ -2220,9 +2270,8 @@ out: RETURN(rc); } -#ifdef __KERNEL__ /* Note: caller will lock/unlock, and set uptodate on the pages */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) static int sanosc_brw_read(struct obd_export *exp, struct obdo *oa, struct lov_stripe_md *lsm, obd_count page_count, struct brw_page *pga) @@ -2495,7 +2544,6 @@ static int sanosc_brw(int cmd, struct obd_export *exp, struct obdo *oa, RETURN(0); } #endif -#endif static void osc_set_data_with_check(struct lustre_handle *lockh, void *data, int flags) @@ -2686,7 +2734,10 @@ static int osc_cancel(struct obd_export *exp, struct lov_stripe_md *md, { ENTRY; - ldlm_lock_decref(lockh, mode); + if (mode == LCK_GROUP) + ldlm_lock_decref_and_cancel(lockh, mode); + else + ldlm_lock_decref(lockh, mode); RETURN(0); } @@ -2796,98 +2847,6 @@ static int osc_getstripe(struct lov_stripe_md *lsm, struct lov_user_md *lump) RETURN(rc); } -static int osc_quotacheck(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct client_obd *cli = &exp->exp_obd->u.cli; - struct ptlrpc_request *req; - struct obd_quotactl *body; - int size = sizeof(*body); - int rc; - ENTRY; - - req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACHECK, 1, &size, - NULL); - if (!req) - GOTO(out, rc = -ENOMEM); - - body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body)); - memcpy(body, oqctl, sizeof(*body)); - - req->rq_replen = lustre_msg_size(0, NULL); - - spin_lock(&cli->cl_qchk_lock); - cli->cl_qchk_stat = CL_QUOTACHECKING; - spin_unlock(&cli->cl_qchk_lock); - - rc = ptlrpc_queue_wait(req); - if (rc) { - spin_lock(&cli->cl_qchk_lock); - cli->cl_qchk_stat = rc; - spin_unlock(&cli->cl_qchk_lock); - } - out: - ptlrpc_req_finished(req); - RETURN (rc); -} - -static int osc_poll_quotacheck(struct obd_export *exp, - struct if_quotacheck *qchk) -{ - struct client_obd *cli = &exp->exp_obd->u.cli; - int stat; - ENTRY; - - spin_lock(&cli->cl_qchk_lock); - stat = cli->cl_qchk_stat; - spin_unlock(&cli->cl_qchk_lock); - - qchk->stat = stat; - if (stat == CL_QUOTACHECKING) { - qchk->stat = -ENODATA; - stat = 0; - } else if (qchk->stat) { - if (qchk->stat > CL_QUOTACHECKING) - qchk->stat = stat = -EINTR; - - strncpy(qchk->obd_type, "obdfilter", 10); - qchk->obd_uuid = cli->cl_import->imp_target_uuid; - } - RETURN(stat); -} - -static int osc_quotactl(struct obd_export *exp, struct obd_quotactl *oqctl) -{ - struct ptlrpc_request *req; - struct obd_quotactl *oqc; - int size = sizeof(*oqctl); - int rc; - ENTRY; - - req = ptlrpc_prep_req(class_exp2cliimp(exp), OST_QUOTACTL, 1, &size, - NULL); - if (!req) - GOTO(out, rc = -ENOMEM); - - memcpy(lustre_msg_buf(req->rq_reqmsg, 0, sizeof (*oqctl)), oqctl, size); - - req->rq_replen = lustre_msg_size(1, &size); - - rc = ptlrpc_queue_wait(req); - if (!rc) { - oqc = lustre_swab_repbuf(req, 0, sizeof (*oqc), - lustre_swab_obd_quotactl); - if (oqc == NULL) { - CERROR ("Can't unpack mds_body\n"); - GOTO(out, rc = -EPROTO); - } - - memcpy(oqctl, oqc, sizeof(*oqctl)); - } -out: - ptlrpc_req_finished(req); - RETURN (rc); -} - static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len, void *karg, void *uarg) { @@ -2918,12 +2877,12 @@ static int osc_iocontrol(unsigned int cmd, struct obd_export *exp, int len, data = (struct obd_ioctl_data *)buf; if (sizeof(*desc) > data->ioc_inllen1) { - OBD_FREE(buf, len); + obd_ioctl_freedata(buf, len); GOTO(out, err = -EINVAL); } if (data->ioc_inllen2 < sizeof(uuid)) { - OBD_FREE(buf, len); + obd_ioctl_freedata(buf, len); GOTO(out, err = -EINVAL); } @@ -3031,8 +2990,9 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen, char *bufs[1] = {key}; ENTRY; - if (keylen == strlen("next_id") && - memcmp(key, "next_id", strlen("next_id")) == 0) { + OBD_FAIL_TIMEOUT(OBD_FAIL_OSC_SHUTDOWN, 10); + + if (KEY_IS("next_id")) { if (vallen != sizeof(obd_id)) RETURN(-EINVAL); obd->u.cli.cl_oscc.oscc_next_id = *((obd_id*)val) + 1; @@ -3043,16 +3003,7 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen, RETURN(0); } - if (keylen == strlen("growth_count") && - memcmp(key, "growth_count", strlen("growth_count")) == 0) { - if (vallen != sizeof(int)) - RETURN(-EINVAL); - obd->u.cli.cl_oscc.oscc_grow_count = *((int*)val); - RETURN(0); - } - - if (keylen == strlen("unlinked") && - memcmp(key, "unlinked", keylen) == 0) { + if (KEY_IS("unlinked")) { struct osc_creator *oscc = &obd->u.cli.cl_oscc; spin_lock(&oscc->oscc_lock); oscc->oscc_flags &= ~OSCC_FLAG_NOSPC; @@ -3061,8 +3012,7 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen, } - if (keylen == strlen("initial_recov") && - memcmp(key, "initial_recov", strlen("initial_recov")) == 0) { + if (KEY_IS("initial_recov")) { struct obd_import *imp = exp->exp_obd->u.cli.cl_import; if (vallen != sizeof(int)) RETURN(-EINVAL); @@ -3073,8 +3023,14 @@ static int osc_set_info(struct obd_export *exp, obd_count keylen, RETURN(0); } - if (keylen < strlen("mds_conn") || - memcmp(key, "mds_conn", strlen("mds_conn")) != 0) + if (KEY_IS("checksum")) { + if (vallen != sizeof(int)) + RETURN(-EINVAL); + exp->exp_obd->u.cli.cl_checksum = (*(int *)val) ? 1 : 0; + RETURN(0); + } + + if (!KEY_IS("mds_conn")) RETURN(-EINVAL); @@ -3252,6 +3208,21 @@ int osc_setup(struct obd_device *obd, obd_count len, void *buf) RETURN(rc); } +static int osc_precleanup(struct obd_device *obd, int stage) +{ + int rc = 0; + ENTRY; + + if (stage < 2) + RETURN(0); + + rc = obd_llog_finish(obd, 0); + if (rc != 0) + CERROR("failed to cleanup llogging subsystems\n"); + + RETURN(rc); +} + int osc_cleanup(struct obd_device *obd) { struct osc_creator *oscc = &obd->u.cli.cl_oscc; @@ -3271,7 +3242,6 @@ int osc_cleanup(struct obd_device *obd) rc = client_obd_cleanup(obd); ptlrpcd_decref(); - obd_llog_finish(obd, 0); RETURN(rc); } @@ -3279,6 +3249,7 @@ int osc_cleanup(struct obd_device *obd) struct obd_ops osc_obd_ops = { .o_owner = THIS_MODULE, .o_setup = osc_setup, + .o_precleanup = osc_precleanup, .o_cleanup = osc_cleanup, .o_add_conn = client_import_add_conn, .o_del_conn = client_import_del_conn, @@ -3319,7 +3290,7 @@ struct obd_ops osc_obd_ops = { .o_quotactl = osc_quotactl, }; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) struct obd_ops sanosc_obd_ops = { .o_owner = THIS_MODULE, .o_cleanup = client_obd_cleanup, @@ -3355,14 +3326,14 @@ struct obd_ops sanosc_obd_ops = { int __init osc_init(void) { struct lprocfs_static_vars lvars; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) struct lprocfs_static_vars sanlvars; #endif int rc; ENTRY; lprocfs_init_vars(osc, &lvars); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) lprocfs_init_vars(osc, &sanlvars); #endif @@ -3371,13 +3342,13 @@ int __init osc_init(void) if (rc) RETURN(rc); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) rc = class_register_type(&sanosc_obd_ops, sanlvars.module_vars, LUSTRE_SANOSC_NAME); if (rc) class_unregister_type(LUSTRE_OSC_NAME); #endif - + rc = osc_qinfo_init(); RETURN(rc); @@ -3387,7 +3358,7 @@ int __init osc_init(void) static void /*__exit*/ osc_exit(void) { osc_qinfo_exit(); -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#if defined(__KERNEL__) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) class_unregister_type(LUSTRE_SANOSC_NAME); #endif class_unregister_type(LUSTRE_OSC_NAME); diff --git a/lustre/ost/autoMakefile.am b/lustre/ost/autoMakefile.am index 72ef6bb..f178425 100644 --- a/lustre/ost/autoMakefile.am +++ b/lustre/ost/autoMakefile.am @@ -7,5 +7,5 @@ if MODULES modulefs_DATA = ost$(KMODEXT) endif -MOSTLYCLEANFILES = *.o *.ko *.mod.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ DIST_SOURCES = $(ost-objs:%.o=%.c) ost_internal.h diff --git a/lustre/ost/lproc_ost.c b/lustre/ost/lproc_ost.c index 4ceec0d..bf64ba2 100644 --- a/lustre/ost/lproc_ost.c +++ b/lustre/ost/lproc_ost.c @@ -26,10 +26,7 @@ #include <linux/seq_file.h> #include "ost_internal.h" -#ifndef LPROCFS -static struct lprocfs_vars lprocfs_obd_vars[] = { {0} }; -static struct lprocfs_vars lprocfs_module_vars[] = { {0} }; -#else +#ifdef LPROCFS static struct lprocfs_vars lprocfs_obd_vars[] = { { "uuid", lprocfs_rd_uuid, 0, 0 }, { 0 } @@ -74,5 +71,5 @@ ost_print_req(void *seq_file, struct ptlrpc_request *req) } } -#endif /* LPROCFS */ LPROCFS_INIT_VARS(ost, lprocfs_module_vars, lprocfs_obd_vars) +#endif /* LPROCFS */ diff --git a/lustre/ost/ost_handler.c b/lustre/ost/ost_handler.c index 2ce8789..c04715d 100644 --- a/lustre/ost/ost_handler.c +++ b/lustre/ost/ost_handler.c @@ -48,8 +48,6 @@ #include <linux/lustre_quota.h> #include "ost_internal.h" -static struct quotacheck_info qchkinfo; - void oti_init(struct obd_trans_info *oti, struct ptlrpc_request *req) { if (oti == NULL) @@ -343,33 +341,31 @@ static void free_per_page_niobufs (int npages, struct niobuf_remote *pp_rnb, OBD_FREE(pp_rnb, sizeof(*pp_rnb) * npages); } -#if CHECKSUM_BULK -obd_count ost_checksum_bulk(struct ptlrpc_bulk_desc *desc) +static __u32 ost_checksum_bulk(struct ptlrpc_bulk_desc *desc) { - obd_count cksum = 0; + __u32 cksum = ~0; int i; for (i = 0; i < desc->bd_iov_count; i++) { +#ifdef CRAY_PORTALS + char *ptr = desc->bd_iov[i].iov_base; + int len = desc->bd_iov[i].iov_len; +#else struct page *page = desc->bd_iov[i].kiov_page; - char *ptr = kmap(page); - int psum, off = desc->bd_iov[i].kiov_offset & ~PAGE_MASK; - int count = desc->bd_iov[i].kiov_len; - - while (count > 0) { - ost_checksum(&cksum, &psum, ptr + off, - count > CHECKSUM_CHUNK ? - CHECKSUM_CHUNK : count); - LL_CDEBUG_PAGE(D_PAGE, page, "off %d checksum %x\n", - off, psum); - off += CHECKSUM_CHUNK; - count -= CHECKSUM_CHUNK; - } + int off = desc->bd_iov[i].kiov_offset & ~PAGE_MASK; + char *ptr = kmap(page) + off; + int len = desc->bd_iov[i].kiov_len; +#endif + + cksum = crc32_le(cksum, ptr, len); +#ifndef CRAY_PORTALS kunmap(page); + LL_CDEBUG_PAGE(D_PAGE, page, "off %d checksum %x\n", off,cksum); +#endif } return cksum; } -#endif static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) { @@ -386,7 +382,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) int npages; int nob = 0; int rc; - int i; + int i, do_checksum; ENTRY; if (OBD_FAIL_CHECK(OBD_FAIL_OST_BRW_READ_BULK)) @@ -408,6 +404,12 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) } niocount = ioo->ioo_bufcnt; + if (niocount > PTLRPC_MAX_BRW_PAGES) { + DEBUG_REQ(D_ERROR, req, "bulk has too many pages (%d)\n", + niocount); + GOTO(out, rc = -EFAULT); + } + remote_nb = lustre_swab_reqbuf(req, 2, niocount * sizeof(*remote_nb), lustre_swab_niobuf_remote); if (remote_nb == NULL) { @@ -444,6 +446,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) GOTO(out_bulk, rc); /* We're finishing using body->oa as an input variable */ + do_checksum = (body->oa.o_valid & OBD_MD_FLCKSUM); body->oa.o_valid = 0; nob = 0; @@ -507,10 +510,12 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) repbody = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repbody)); memcpy(&repbody->oa, &body->oa, sizeof(repbody->oa)); -#if CHECKSUM_BULK - repbody->oa.o_cksum = ost_checksum_bulk(desc); - repbody->oa.o_valid |= OBD_MD_FLCKSUM; -#endif + if (unlikely(do_checksum)) { + repbody->oa.o_cksum = ost_checksum_bulk(desc); + repbody->oa.o_valid |= OBD_MD_FLCKSUM; + CDEBUG(D_PAGE, "checksum at read origin: %x\n", + repbody->oa.o_cksum); + } } out_bulk: @@ -542,7 +547,7 @@ static int ost_brw_read(struct ptlrpc_request *req, struct obd_trans_info *oti) req->rq_export->exp_client_uuid.uuid, req->rq_export->exp_connection->c_remote_uuid.uuid, libcfs_id2str(req->rq_peer)); - ptlrpc_fail_export(req->rq_export); + class_fail_export(req->rq_export); } else { CERROR("ignoring bulk IO comms error: " "client reconnected %s@%s id %s\n", @@ -568,7 +573,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) int size[2] = { sizeof(*body) }; int objcount, niocount, npages; int comms_error = 0; - int rc, swab, i, j; + int rc, swab, i, j, do_checksum; ENTRY; if (OBD_FAIL_CHECK(OBD_FAIL_OST_BRW_WRITE_BULK)) @@ -591,6 +596,11 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) CERROR("Missing/short ioobj\n"); GOTO(out, rc = -EFAULT); } + if (objcount > 1) { + CERROR("too many ioobjs (%d)\n", objcount); + GOTO(out, rc = -EFAULT); + } + ioo = lustre_msg_buf (req->rq_reqmsg, 1, objcount * sizeof(*ioo)); LASSERT (ioo != NULL); for (niocount = i = 0; i < objcount; i++) { @@ -603,6 +613,12 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) niocount += ioo[i].ioo_bufcnt; } + if (niocount > PTLRPC_MAX_BRW_PAGES) { + DEBUG_REQ(D_ERROR, req, "bulk has too many pages (%d)\n", + niocount); + GOTO(out, rc = -EFAULT); + } + remote_nb = lustre_swab_reqbuf(req, 2, niocount * sizeof(*remote_nb), lustre_swab_niobuf_remote); if (remote_nb == NULL) { @@ -635,6 +651,9 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) if (desc == NULL) GOTO(out_local, rc = -ENOMEM); + /* obd_preprw clobbers oa->valid, so save what we need */ + do_checksum = (body->oa.o_valid & OBD_MD_FLCKSUM); + rc = obd_preprw(OBD_BRW_WRITE, req->rq_export, &body->oa, objcount, ioo, npages, pp_rnb, local_nb, oti); if (rc != 0) @@ -674,18 +693,23 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) repbody = lustre_msg_buf(req->rq_repmsg, 0, sizeof(*repbody)); memcpy(&repbody->oa, &body->oa, sizeof(repbody->oa)); -#if CHECKSUM_BULK - if (rc == 0 && (body->oa.o_valid & OBD_MD_FLCKSUM) != 0) { + if (unlikely(do_checksum && rc == 0)) { static int cksum_counter; obd_count client_cksum = body->oa.o_cksum; obd_count cksum = ost_checksum_bulk(desc); + cksum_counter++; if (client_cksum != cksum) { CERROR("Bad checksum: client %x, server %x id %s\n", client_cksum, cksum, libcfs_id2str(req->rq_peer)); - cksum_counter = 1; + cksum_counter = 0; repbody->oa.o_cksum = cksum; + repbody->oa.o_valid |= OBD_MD_FLCKSUM; + } else if ((cksum_counter & (-cksum_counter)) == + cksum_counter) { + CWARN("Checksum %u from %s: %x OK\n", cksum_counter, + libcfs_id2str(req->rq_peer), cksum); } else { cksum_counter++; if ((cksum_counter & (-cksum_counter)) == cksum_counter) @@ -694,7 +718,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) libcfs_id2str(req->rq_peer), cksum); } } -#endif + /* Must commit after prep above in all cases */ rc = obd_commitrw(OBD_BRW_WRITE, req->rq_export, &repbody->oa, objcount, ioo, npages, local_nb, oti, rc); @@ -745,7 +769,7 @@ static int ost_brw_write(struct ptlrpc_request *req, struct obd_trans_info *oti) req->rq_export->exp_client_uuid.uuid, req->rq_export->exp_connection->c_remote_uuid.uuid, libcfs_id2str(req->rq_peer)); - ptlrpc_fail_export(req->rq_export); + class_fail_export(req->rq_export); } else { CERROR("ignoring bulk IO comms error: " "client reconnected %s@%s id %s\n", @@ -915,131 +939,6 @@ static int ost_filter_recovery_request(struct ptlrpc_request *req, } } -static int ost_quotacheck_callback(struct obd_export *exp, - struct obd_quotactl *oqctl) -{ - struct ptlrpc_request *req; - struct obd_quotactl *body; - int rc, size = sizeof(*oqctl); - - req = ptlrpc_prep_req(exp->exp_imp_reverse, OBD_QC_CALLBACK, - 1, &size, NULL); - if (!req) - RETURN(-ENOMEM); - - body = lustre_msg_buf(req->rq_reqmsg, 0, sizeof(*body)); - memcpy(body, oqctl, sizeof(*oqctl)); - - req->rq_replen = lustre_msg_size(0, NULL); - - rc = ptlrpc_queue_wait(req); - ptlrpc_req_finished(req); - - RETURN(rc); -} - -static int ost_quotacheck_thread(void *data) -{ - unsigned long flags; - struct quotacheck_info *qchki = data; - struct obd_export *exp; - struct obd_quotactl *oqctl; - struct filter_obd *filter; - int rc; - - lock_kernel(); - ptlrpc_daemonize(); - - SIGNAL_MASK_LOCK(current, flags); - sigfillset(¤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, }; diff --git a/lustre/ost/ost_internal.h b/lustre/ost/ost_internal.h index f7cd79a..9f31f25 100644 --- a/lustre/ost/ost_internal.h +++ b/lustre/ost/ost_internal.h @@ -11,4 +11,21 @@ extern void ost_print_req(void *seq_file, struct ptlrpc_request *req); # define ost_print_req NULL #endif +#ifdef HAVE_QUOTA_SUPPORT +/* Quota stuff */ +int ost_quotacheck(struct ptlrpc_request *req); +int ost_quotactl(struct ptlrpc_request *req); +#else +static inline int ost_quotacheck(struct ptlrpc_request *req) +{ + req->rq_status = -ENOTSUPP; + return -ENOTSUPP; +} +static inline int ost_quotactl(struct ptlrpc_request *req) +{ + req->rq_status = -ENOTSUPP; + return -ENOTSUPP; +} +#endif + #endif /* OST_INTERNAL_H */ diff --git a/lustre/ptlrpc/Makefile.in b/lustre/ptlrpc/Makefile.in index 54f5a9d..002e5ef 100644 --- a/lustre/ptlrpc/Makefile.in +++ b/lustre/ptlrpc/Makefile.in @@ -15,6 +15,11 @@ ptlrpc_objs += llog_net.o llog_client.o llog_server.o import.o ptlrpcd.o ptlrpc_objs += pers.o lproc_ptlrpc.o ptlrpc-objs := $(ldlm_objs) $(ptlrpc_objs) + +ifeq ($(PATCHLEVEL),6) +#ptlrpc-objs += @top_srcdir@/lustre/mds/quota_context.o +endif + default: all ldlm_%.c: @LUSTRE@/ldlm/ldlm_%.c diff --git a/lustre/ptlrpc/autoMakefile.am b/lustre/ptlrpc/autoMakefile.am index 4a9f686..c77dcfb 100644 --- a/lustre/ptlrpc/autoMakefile.am +++ b/lustre/ptlrpc/autoMakefile.am @@ -11,11 +11,12 @@ LDLM_COMM_SOURCES= $(top_srcdir)/lustre/ldlm/l_lock.c \ $(top_srcdir)/lustre/ldlm/ldlm_extent.c \ $(top_srcdir)/lustre/ldlm/ldlm_request.c \ $(top_srcdir)/lustre/ldlm/ldlm_lockd.c \ - $(top_srcdir)/lustre/ldlm/ldlm_internal.h + $(top_srcdir)/lustre/ldlm/ldlm_internal.h \ + $(top_srcdir)/lustre/ldlm/ldlm_flock.c COMMON_SOURCES = client.c recover.c connection.c niobuf.c pack_generic.c \ events.c ptlrpc_module.c service.c pinger.c recov_thread.c llog_net.c \ - llog_client.c llog_server.c import.c ptlrpcd.c \ + llog_client.c llog_server.c import.c ptlrpcd.c pers.c \ ptlrpc_internal.h $(LDLM_COMM_SOURCES) if LIBLUSTRE @@ -31,5 +32,5 @@ if MODULES modulefs_DATA = ptlrpc$(KMODEXT) endif # MODULES -MOSTLYCLEANFILES = *.o *.ko *.mod.c ldlm_*.c l_lock.c +MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ ldlm_*.c l_lock.c DIST_SOURCES = $(ptlrpc_objs:.o=.c) ptlrpc_internal.h diff --git a/lustre/ptlrpc/client.c b/lustre/ptlrpc/client.c index 502b99a..db0dc82 100644 --- a/lustre/ptlrpc/client.c +++ b/lustre/ptlrpc/client.c @@ -97,7 +97,7 @@ static inline struct ptlrpc_bulk_desc *new_bulk(int npages, int type, int portal desc->bd_md_h = PTL_INVALID_HANDLE; desc->bd_portal = portal; desc->bd_type = type; - + return desc; } @@ -176,7 +176,7 @@ void ptlrpc_free_bulk(struct ptlrpc_bulk_desc *desc) else class_import_put(desc->bd_import); - OBD_FREE(desc, offsetof(struct ptlrpc_bulk_desc, + OBD_FREE(desc, offsetof(struct ptlrpc_bulk_desc, bd_iov[desc->bd_max_iov])); EXIT; } @@ -189,6 +189,9 @@ struct ptlrpc_request *ptlrpc_prep_req(struct obd_import *imp, int opcode, ENTRY; LASSERT((unsigned long)imp > 0x1000); + LASSERT(imp != LP_POISON); + LASSERT((unsigned long)imp->imp_client > 0x1000); + LASSERT(imp->imp_client != LP_POISON); OBD_ALLOC(request, sizeof(*request)); if (!request) { @@ -211,13 +214,13 @@ struct ptlrpc_request *ptlrpc_prep_req(struct obd_import *imp, int opcode, request->rq_type = PTL_RPC_MSG_REQUEST; request->rq_import = class_import_get(imp); request->rq_export = NULL; - + request->rq_req_cbid.cbid_fn = request_out_callback; request->rq_req_cbid.cbid_arg = request; request->rq_reply_cbid.cbid_fn = reply_in_callback; request->rq_reply_cbid.cbid_arg = request; - + request->rq_phase = RQ_PHASE_NEW; /* XXX FIXME bug 249 */ @@ -342,7 +345,7 @@ void ptlrpc_set_add_new_req(struct ptlrpc_request_set *set, * * The imp->imp_lock must be held. */ -static int ptlrpc_import_delay_req(struct obd_import *imp, +static int ptlrpc_import_delay_req(struct obd_import *imp, struct ptlrpc_request *req, int *status) { int delay = 0; @@ -355,31 +358,24 @@ static int ptlrpc_import_delay_req(struct obd_import *imp, DEBUG_REQ(D_ERROR, req, "Uninitialized import."); *status = -EIO; LBUG(); - } - else if (imp->imp_state == LUSTRE_IMP_CLOSED) { + } else if (imp->imp_state == LUSTRE_IMP_CLOSED) { DEBUG_REQ(D_ERROR, req, "IMP_CLOSED "); *status = -EIO; - } - /* allow CONNECT even if import is invalid */ - else if (req->rq_send_state == LUSTRE_IMP_CONNECTING && + } else if (req->rq_send_state == LUSTRE_IMP_CONNECTING && imp->imp_state == LUSTRE_IMP_CONNECTING) { - ; - } - /* - * If the import has been invalidated (such as by an OST failure), the - * request must fail with -EIO. - */ - else if (imp->imp_invalid) { - DEBUG_REQ(D_ERROR, req, "IMP_INVALID"); + /* allow CONNECT even if import is invalid */ ; + } else if (imp->imp_invalid) { + /* If the import has been invalidated (such as by an OST + * failure), the request must fail with -EIO. */ + if (!imp->imp_deactive) + DEBUG_REQ(D_ERROR, req, "IMP_INVALID"); *status = -EIO; - } - else if (req->rq_import_generation != imp->imp_generation) { + } else if (req->rq_import_generation != imp->imp_generation) { DEBUG_REQ(D_ERROR, req, "req wrong generation:"); *status = -EIO; - } - else if (req->rq_send_state != imp->imp_state) { - if (imp->imp_obd->obd_no_recov || imp->imp_dlm_fake - || req->rq_no_delay) + } else if (req->rq_send_state != imp->imp_state) { + if (imp->imp_obd->obd_no_recov || imp->imp_dlm_fake || + req->rq_no_delay) *status = -EWOULDBLOCK; else delay = 1; @@ -401,10 +397,10 @@ static int ptlrpc_check_reply(struct ptlrpc_request *req) DEBUG_REQ(D_NET, req, "REPLIED:"); GOTO(out, rc = 1); } - + if (req->rq_net_err && !req->rq_timedout) { spin_unlock_irqrestore (&req->rq_lock, flags); - rc = ptlrpc_expire_one_request(req); + rc = ptlrpc_expire_one_request(req); spin_lock_irqsave (&req->rq_lock, flags); GOTO(out, rc); } @@ -437,7 +433,7 @@ static int ptlrpc_check_status(struct ptlrpc_request *req) err = req->rq_repmsg->status; if (req->rq_repmsg->type == PTL_RPC_MSG_ERR) { - DEBUG_REQ(D_ERROR, req, "type == PTL_RPC_MSG_ERR, err == %d", + DEBUG_REQ(D_ERROR, req, "type == PTL_RPC_MSG_ERR, err == %d", err); RETURN(err < 0 ? err : -EINVAL); } @@ -471,14 +467,14 @@ static int after_reply(struct ptlrpc_request *req) LASSERT (req->rq_nob_received <= req->rq_replen); rc = lustre_unpack_msg(req->rq_repmsg, req->rq_nob_received); if (rc) { - CERROR("unpack_rep failed: %d\n", rc); + DEBUG_REQ(D_ERROR, req, "unpack_rep failed: %d\n", rc); RETURN(-EPROTO); } if (req->rq_repmsg->type != PTL_RPC_MSG_REPLY && req->rq_repmsg->type != PTL_RPC_MSG_ERR) { - CERROR("invalid packet type received (type=%u)\n", - req->rq_repmsg->type); + DEBUG_REQ(D_ERROR, req, "invalid packet received (type=%u)\n", + req->rq_repmsg->type); RETURN(-EPROTO); } @@ -544,7 +540,7 @@ static int ptlrpc_send_new_req(struct ptlrpc_request *req) DEBUG_REQ(D_HA, req, "req from PID %d waiting for recovery: " "(%s != %s)", - req->rq_reqmsg->status, + req->rq_reqmsg->status, ptlrpc_import_state_name(req->rq_send_state), ptlrpc_import_state_name(imp->imp_state)); LASSERT(list_empty (&req->rq_list)); @@ -619,7 +615,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set) GOTO(interpret, req->rq_status); if (req->rq_net_err && !req->rq_timedout) - ptlrpc_expire_one_request(req); + ptlrpc_expire_one_request(req); if (req->rq_err) { ptlrpc_unregister_reply(req); @@ -637,7 +633,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set) /* ptlrpc_queue_wait->l_wait_event guarantees that rq_intr * will only be set after rq_timedout, but the oig waiting * path sets rq_intr irrespective of whether ptlrpcd has - * seen a timeout. our policy is to only interpret + * seen a timeout. our policy is to only interpret * interrupted rpcs after they have timed out */ if (req->rq_intr && (req->rq_timedout || req->rq_waiting)) { /* NB could be on delayed list */ @@ -733,7 +729,7 @@ int ptlrpc_check_set(struct ptlrpc_request_set *set) it can be errored if the import is evicted after recovery. */ spin_lock_irqsave (&req->rq_lock, flags); - list_add_tail(&req->rq_list, + list_add_tail(&req->rq_list, &imp->imp_delayed_list); spin_unlock_irqrestore(&req->rq_lock, flags); continue; @@ -862,8 +858,8 @@ int ptlrpc_expired_set(void *data) list_entry(tmp, struct ptlrpc_request, rq_set_chain); /* request in-flight? */ - if (!((req->rq_phase == RQ_PHASE_RPC && !req->rq_waiting - && !req->rq_resend) || + if (!((req->rq_phase == RQ_PHASE_RPC && !req->rq_waiting && + !req->rq_resend) || (req->rq_phase == RQ_PHASE_BULK))) continue; @@ -1345,8 +1341,8 @@ restart: spin_unlock_irqrestore(&imp->imp_lock, flags); DEBUG_REQ(D_HA, req, "\"%s\" waiting for recovery: (%s != %s)", - current->comm, - ptlrpc_import_state_name(req->rq_send_state), + current->comm, + ptlrpc_import_state_name(req->rq_send_state), ptlrpc_import_state_name(imp->imp_state)); lwi = LWI_INTR(interrupted_request, req); rc = l_wait_event(req->rq_reply_waitq, @@ -1354,8 +1350,8 @@ restart: req->rq_err), &lwi); DEBUG_REQ(D_HA, req, "\"%s\" awake: (%s == %s or %d == 1)", - current->comm, - ptlrpc_import_state_name(imp->imp_state), + current->comm, + ptlrpc_import_state_name(imp->imp_state), ptlrpc_import_state_name(req->rq_send_state), req->rq_err); @@ -1387,9 +1383,19 @@ restart: if (req->rq_resend) { lustre_msg_add_flags(req->rq_reqmsg, MSG_RESENT); - if (req->rq_bulk != NULL) + if (req->rq_bulk != NULL) { ptlrpc_unregister_bulk (req); + /* bulk requests are supposed to be + * idempotent, so we are free to bump the xid + * here, which we need to do before + * registering the bulk again (bug 6371). + * print the old xid first for sanity. + */ + DEBUG_REQ(D_HA, req, "bumping xid for bulk: "); + req->rq_xid = ptlrpc_next_xid(); + } + DEBUG_REQ(D_HA, req, "resending: "); } @@ -1467,7 +1473,7 @@ restart: out: if (req->rq_bulk != NULL) { - if (rc >= 0) { + if (rc >= 0) { /* success so far. Note that anything going wrong * with bulk now, is EXTREMELY strange, since the * server must have believed that the bulk @@ -1512,7 +1518,7 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req, unsigned long flags; atomic_dec(&imp->imp_replay_inflight); - + if (!req->rq_replied) { CERROR("request replay timed out, restarting recovery\n"); GOTO(out, rc = -ETIMEDOUT); @@ -1525,12 +1531,12 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req, LASSERT (req->rq_nob_received <= req->rq_replen); rc = lustre_unpack_msg(req->rq_repmsg, req->rq_nob_received); if (rc) { - CERROR("unpack_rep failed: %d\n", rc); + DEBUG_REQ(D_ERROR, req, "unpack_rep failed: %d\n", rc); GOTO(out, rc = -EPROTO); } - if (req->rq_repmsg->type == PTL_RPC_MSG_ERR && - req->rq_repmsg->status == -ENOTCONN) + if (req->rq_repmsg->type == PTL_RPC_MSG_ERR && + req->rq_repmsg->status == -ENOTCONN) GOTO(out, rc = req->rq_repmsg->status); /* The transno had better not change over replay. */ @@ -1558,7 +1564,7 @@ static int ptlrpc_replay_interpret(struct ptlrpc_request *req, rc = ptlrpc_import_recovery_state_machine(imp); out: req->rq_send_state = aa->praa_old_state; - + if (rc != 0) /* this replay failed, so restart recovery */ ptlrpc_connect_import(imp, NULL); diff --git a/lustre/ptlrpc/events.c b/lustre/ptlrpc/events.c index 928f268..f1ef597 100644 --- a/lustre/ptlrpc/events.c +++ b/lustre/ptlrpc/events.c @@ -211,6 +211,9 @@ void request_in_callback(ptl_event_t *ev) req->rq_peer = ev->initiator; req->rq_rqbd = rqbd; req->rq_phase = RQ_PHASE_NEW; +#if CRAY_PORTALS + req->rq_uid = ev->uid; +#endif spin_lock_irqsave (&service->srv_lock, flags); @@ -393,6 +396,12 @@ ptl_pid_t ptl_get_pid(void) #ifndef __KERNEL__ pid = getpid(); +# if CRAY_PORTALS + /* hack to keep pid in range accepted by ernal */ + pid &= 0xFF; + if (pid == LUSTRE_SRV_PTL_PID) + pid++; +# endif #else pid = LUSTRE_SRV_PTL_PID; #endif @@ -404,7 +413,7 @@ int ptlrpc_ni_init(void) int rc; char str[20]; ptl_pid_t pid; - + pid = ptl_get_pid(); /* We're not passing any limits yet... */ @@ -425,6 +434,11 @@ int ptlrpc_ni_init(void) /* kernel portals calls our master callback when events are added to * the event queue. In fact lustre never pulls events off this queue, * so it's only sized for some debug history. */ +# if CRAY_PORTALS + rc = PtlNIDebug(pni->pni_ni_h, 0xffffffff); + if (rc != PTL_OK) + CDEBUG(D_ERROR, "Can't enable Cray Portals Debug: rc %d\n", rc); +# endif rc = PtlEQAlloc(ptlrpc_ni_h, 1024, ptlrpc_master_callback, &ptlrpc_eq_h); #else @@ -487,7 +501,7 @@ liblustre_check_events (int timeout) int i; ENTRY; - rc = PtlEQPoll(ptlrpc_eq_h, 1, timeout * 1000, &ev, &i); + rc = PtlEQPoll(&ptlrpc_eq_h, 1, timeout * 1000, &ev, &i); if (rc == PTL_EQ_EMPTY) RETURN(0); diff --git a/lustre/ptlrpc/import.c b/lustre/ptlrpc/import.c index 6cb508b..0b0ebf5 100644 --- a/lustre/ptlrpc/import.c +++ b/lustre/ptlrpc/import.c @@ -241,14 +241,10 @@ void ptlrpc_fail_import(struct obd_import *imp, int generation) EXIT; } -#define ATTEMPT_TOO_SOON(last) \ - ((last) && ((long)(jiffies - (last)) <= (long)(obd_timeout * 2 * HZ))) - static int import_select_connection(struct obd_import *imp) { - struct obd_import_conn *imp_conn, *tmp; + struct obd_import_conn *imp_conn; struct obd_export *dlmexp; - int found = 0; ENTRY; spin_lock(&imp->imp_lock); @@ -260,34 +256,13 @@ static int import_select_connection(struct obd_import *imp) RETURN(-EINVAL); } - list_for_each_entry(imp_conn, &imp->imp_conn_list, oic_item) { - if (!imp_conn->oic_last_attempt || - time_after(jiffies, imp_conn->oic_last_attempt + - obd_timeout * 2 * HZ)) { - found = 1; - break; - } - } - - /* if not found, simply choose the current one */ - if (!found) { - CWARN("%s: continuing with current connection\n", - imp->imp_obd->obd_name); - LASSERT(imp->imp_conn_current); - imp_conn = imp->imp_conn_current; - } - LASSERT(imp_conn->oic_conn); - - imp_conn->oic_last_attempt = jiffies; - - /* move the items ahead of the selected one to list tail */ - while (1) { - tmp= list_entry(imp->imp_conn_list.next, - struct obd_import_conn, oic_item); - if (tmp == imp_conn) - break; - list_del(&tmp->oic_item); - list_add_tail(&tmp->oic_item, &imp->imp_conn_list); + if (imp->imp_conn_current && + !(imp->imp_conn_current->oic_item.next == &imp->imp_conn_list)) { + imp_conn = list_entry(imp->imp_conn_current->oic_item.next, + struct obd_import_conn, oic_item); + } else { + imp_conn = list_entry(imp->imp_conn_list.next, + struct obd_import_conn, oic_item); } /* switch connection, don't mind if it's same as the current one */ @@ -303,9 +278,8 @@ static int import_select_connection(struct obd_import *imp) class_export_put(dlmexp); imp->imp_conn_current = imp_conn; - CWARN("%s: Using connection %s\n", - imp->imp_obd->obd_name, - imp_conn->oic_uuid.uuid); + CDEBUG(D_HA, "%s: import %p using connection %s\n", + imp->imp_obd->obd_name, imp, imp_conn->oic_uuid.uuid); spin_unlock(&imp->imp_lock); RETURN(0); @@ -404,6 +378,36 @@ out: RETURN(rc); } +static void ptlrpc_maybe_ping_import_soon(struct obd_import *imp) +{ + struct obd_import_conn *imp_conn; + unsigned long flags; + int wake_pinger = 0; + + ENTRY; + + spin_lock_irqsave(&imp->imp_lock, flags); + if (list_empty(&imp->imp_conn_list)) + GOTO(unlock, 0); + + imp_conn = list_entry(imp->imp_conn_list.prev, + struct obd_import_conn, + oic_item); + + if (imp->imp_conn_current != imp_conn) { + ptlrpc_ping_import_soon(imp); + wake_pinger = 1; + } + + unlock: + spin_unlock_irqrestore(&imp->imp_lock, flags); + + if (wake_pinger) + ptlrpc_pinger_wake_up(); + + EXIT; +} + static int ptlrpc_connect_interpret(struct ptlrpc_request *request, void * data, int rc) { @@ -425,19 +429,22 @@ static int ptlrpc_connect_interpret(struct ptlrpc_request *request, GOTO(out, rc); LASSERT(imp->imp_conn_current); - imp->imp_conn_current->oic_last_attempt = 0; msg_flags = lustre_msg_get_op_flags(request->rq_repmsg); + /* All imports are pingable */ + imp->imp_pingable = 1; + if (aa->pcaa_initial_connect) { if (msg_flags & MSG_CONNECT_REPLAYABLE) { CDEBUG(D_HA, "connected to replayable target: %s\n", imp->imp_target_uuid.uuid); - imp->imp_pingable = imp->imp_replayable = 1; + imp->imp_replayable = 1; } else { imp->imp_replayable = 0; } imp->imp_remote_handle = request->rq_repmsg->handle; + IMPORT_SET_STATE(imp, LUSTRE_IMP_FULL); GOTO(finish, rc = 0); } @@ -516,13 +523,22 @@ finish: ptlrpc_connect_import(imp, NULL); RETURN(0); } + } else { + list_del(&imp->imp_conn_current->oic_item); + list_add(&imp->imp_conn_current->oic_item, + &imp->imp_conn_list); + imp->imp_conn_current = NULL; } + out: if (rc != 0) { IMPORT_SET_STATE(imp, LUSTRE_IMP_DISCON); if (aa->pcaa_initial_connect && !imp->imp_initial_recov) { ptlrpc_deactivate_import(imp); } + + ptlrpc_maybe_ping_import_soon(imp); + CDEBUG(D_HA, "recovery of %s on %s failed (%d)\n", imp->imp_target_uuid.uuid, (char *)imp->imp_connection->c_remote_uuid.uuid, rc); @@ -572,6 +588,7 @@ static int signal_completed_replay(struct obd_import *imp) RETURN(0); } +#ifdef __KERNEL__ static int ptlrpc_invalidate_import_thread(void *data) { struct obd_import *imp = data; @@ -600,6 +617,7 @@ static int ptlrpc_invalidate_import_thread(void *data) RETURN(0); } +#endif int ptlrpc_import_recovery_state_machine(struct obd_import *imp) { @@ -612,20 +630,25 @@ int ptlrpc_import_recovery_state_machine(struct obd_import *imp) deuuidify(imp->imp_target_uuid.uuid, NULL, &target_start, &target_len); LCONSOLE_ERROR("This client was evicted by %.*s; in progress " - "operations using this service will %s.\n", - target_len, target_start, - imp->imp_replayable - ? "be reattempted" - : "fail"); + "operations using this service will fail.\n", + target_len, target_start); CDEBUG(D_HA, "evicted from %s@%s; invalidating\n", imp->imp_target_uuid.uuid, imp->imp_connection->c_remote_uuid.uuid); +#ifdef __KERNEL__ rc = kernel_thread(ptlrpc_invalidate_import_thread, imp, CLONE_VM | CLONE_FILES); if (rc < 0) CERROR("error starting invalidate thread: %d\n", rc); + else + rc = 0; RETURN(rc); +#else + ptlrpc_invalidate_import(imp); + + IMPORT_SET_STATE(imp, LUSTRE_IMP_RECOVER); +#endif } if (imp->imp_state == LUSTRE_IMP_REPLAY) { diff --git a/lustre/ptlrpc/llog_client.c b/lustre/ptlrpc/llog_client.c index 591c5fc..5ea8bca 100644 --- a/lustre/ptlrpc/llog_client.c +++ b/lustre/ptlrpc/llog_client.c @@ -57,7 +57,13 @@ static int llog_client_create(struct llog_ctxt *ctxt, struct llog_handle **res, int rc; ENTRY; - LASSERT(ctxt->loc_imp); + if (ctxt->loc_imp == NULL) { + /* This used to be an assert; bug 6200 */ + CERROR("ctxt->loc_imp == NULL for context idx %d. Unable to " + "complete MDS/OSS recovery, but I'll try again next " + "time. Not fatal.\n", ctxt->loc_idx); + RETURN(-EINVAL); + } imp = ctxt->loc_imp; handle = llog_alloc_handle(); diff --git a/lustre/ptlrpc/llog_server.c b/lustre/ptlrpc/llog_server.c index a038d8a..5c7af41 100644 --- a/lustre/ptlrpc/llog_server.c +++ b/lustre/ptlrpc/llog_server.c @@ -50,7 +50,7 @@ int llog_origin_handle_create(struct ptlrpc_request *req) struct obd_device *disk_obd; struct llog_handle *loghandle; struct llogd_body *body; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_logid *logid = NULL; struct llog_ctxt *ctxt; char * name = NULL; @@ -81,7 +81,7 @@ int llog_origin_handle_create(struct ptlrpc_request *req) if (ctxt == NULL) GOTO(out, rc = -EINVAL); disk_obd = ctxt->loc_exp->exp_obd; - push_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); rc = llog_create(ctxt, &loghandle, logid, name); if (rc) @@ -99,7 +99,7 @@ out_close: if (!rc) rc = rc2; out_pop: - pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); out: RETURN(rc); } @@ -111,7 +111,7 @@ int llog_origin_handle_next_block(struct ptlrpc_request *req) struct obd_device *disk_obd; struct llog_handle *loghandle; struct llogd_body *body; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_ctxt *ctxt; __u32 flags; __u8 *buf; @@ -136,7 +136,7 @@ int llog_origin_handle_next_block(struct ptlrpc_request *req) if (ctxt == NULL) GOTO(out, rc = -EINVAL); disk_obd = ctxt->loc_exp->exp_obd; - push_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); rc = llog_create(ctxt, &loghandle, &body->lgd_logid, NULL); if (rc) @@ -171,7 +171,7 @@ out_close: rc = rc2; out_pop: - pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); OBD_FREE(buf, LLOG_CHUNK_SIZE); out: RETURN(rc); @@ -185,7 +185,7 @@ int llog_origin_handle_read_header(struct ptlrpc_request *req) struct llog_handle *loghandle; struct llogd_body *body; struct llog_log_hdr *hdr; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_ctxt *ctxt; __u32 flags; int size[] = {sizeof (*hdr)}; @@ -203,7 +203,7 @@ int llog_origin_handle_read_header(struct ptlrpc_request *req) if (ctxt == NULL) GOTO(out, rc = -EINVAL); disk_obd = ctxt->loc_exp->exp_obd; - push_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); rc = llog_create(ctxt, &loghandle, &body->lgd_logid, NULL); if (rc) @@ -228,7 +228,7 @@ out_close: rc = rc2; out_pop: - pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); out: RETURN(rc); @@ -250,7 +250,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req) struct llog_cookie *logcookies; struct llog_ctxt *ctxt; int num_cookies, rc = 0, err, i; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_handle *cathandle; struct inode *inode; void *handle; @@ -270,7 +270,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req) } disk_obd = ctxt->loc_exp->exp_obd; - push_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + push_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); for (i = 0; i < num_cookies; i++, logcookies++) { cathandle = ctxt->loc_handle; LASSERT(cathandle != NULL); @@ -294,7 +294,7 @@ int llog_origin_handle_cancel(struct ptlrpc_request *req) } } pop_ctxt: - pop_ctxt(&saved, &disk_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &disk_obd->obd_lvfs_ctxt, NULL); if (rc) CERROR("cancel %d llog-records failed: %d\n", num_cookies, rc); else @@ -309,7 +309,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len, { struct mds_obd *mds = &obd->u.mds; struct llog_ctxt *ctxt = llog_get_context(obd, LLOG_CONFIG_ORIG_CTXT); - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; struct llog_handle *handle = NULL; char name[4][64]; int rc, i, l, remains = buf_len; @@ -318,7 +318,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len, if (ctxt == NULL || mds == NULL) RETURN(-EOPNOTSUPP); - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); sprintf(name[0], "%s", mds->mds_profile); sprintf(name[1], "%s-clean", mds->mds_profile); @@ -354,7 +354,7 @@ static int llog_catinfo_config(struct obd_device *obd, char *buf, int buf_len, break; } out_pop: - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); RETURN(rc); } @@ -426,7 +426,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf, { struct mds_obd *mds = &obd->u.mds; struct llog_handle *handle; - struct obd_run_ctxt saved; + struct lvfs_run_ctxt saved; int size, i, count; struct llog_catid *idarray; struct llog_logid *id; @@ -449,7 +449,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf, if (rc) GOTO(out_free, rc); - push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + push_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); data.ctxt = ctxt; data.out = buf; @@ -487,7 +487,7 @@ static int llog_catinfo_deletions(struct obd_device *obd, char *buf, break; } out_pop: - pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_ctxt, NULL); + pop_ctxt(&saved, &ctxt->loc_exp->exp_obd->obd_lvfs_ctxt, NULL); out_free: OBD_FREE(idarray, size); RETURN(rc); diff --git a/lustre/ptlrpc/lproc_ptlrpc.c b/lustre/ptlrpc/lproc_ptlrpc.c index aa208ce..6568579 100644 --- a/lustre/ptlrpc/lproc_ptlrpc.c +++ b/lustre/ptlrpc/lproc_ptlrpc.c @@ -102,12 +102,7 @@ const char* ll_opcode2str(__u32 opcode) return ll_rpc_opcode_table[offset].opname; } -#ifndef LPROCFS -void ptlrpc_lprocfs_register_service(struct obd_device *obddev, - struct ptlrpc_service *svc) { return ; } -void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc) { return; } -#else - +#ifdef LPROCFS void ptlrpc_lprocfs_register(struct proc_dir_entry *root, char *dir, char *name, struct proc_dir_entry **procroot_ret, struct lprocfs_stats **stats_ret) @@ -167,7 +162,7 @@ ptlrpc_lprocfs_read_req_history_len(char *page, char **start, off_t off, int count, int *eof, void *data) { struct ptlrpc_service *svc = data; - + *eof = 1; return snprintf(page, count, "%d\n", svc->srv_n_history_rqbds); } @@ -177,7 +172,7 @@ ptlrpc_lprocfs_read_req_history_max(char *page, char **start, off_t off, int count, int *eof, void *data) { struct ptlrpc_service *svc = data; - + *eof = 1; return snprintf(page, count, "%d\n", svc->srv_max_history_rqbds); } @@ -191,7 +186,7 @@ ptlrpc_lprocfs_write_req_history_max(struct file *file, const char *buffer, unsigned long flags; int val; int rc = lprocfs_write_helper(buffer, count, &val); - + if (rc < 0) return rc; @@ -204,11 +199,11 @@ ptlrpc_lprocfs_write_req_history_max(struct file *file, const char *buffer, bufpages = (svc->srv_buf_size + PAGE_SIZE - 1)/PAGE_SIZE; if (val > num_physpages/(2*bufpages)) return -ERANGE; - + spin_lock_irqsave(&svc->srv_lock, flags); svc->srv_max_history_rqbds = val; spin_unlock_irqrestore(&svc->srv_lock, flags); - + return count; } @@ -230,7 +225,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc, srhi->srhi_seq <= seq) { /* If srhi_req was set previously, hasn't been culled and * we're searching for a seq on or after it (i.e. more - * recent), search from it onwards. + * recent), search from it onwards. * Since the service history is LRU (i.e. culled reqs will * be near the head), we shouldn't have to do long * re-scans */ @@ -244,7 +239,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc, while (e != &svc->srv_request_history) { req = list_entry(e, struct ptlrpc_request, rq_history_list); - + if (req->rq_history_seq >= seq) { srhi->srhi_seq = req->rq_history_seq; srhi->srhi_req = req; @@ -252,7 +247,7 @@ ptlrpc_lprocfs_svc_req_history_seek(struct ptlrpc_service *svc, } e = e->next; } - + return -ENOENT; } @@ -270,16 +265,16 @@ ptlrpc_lprocfs_svc_req_history_start(struct seq_file *s, loff_t *pos) srhi->srhi_seq = 0; srhi->srhi_req = NULL; - + spin_lock_irqsave(&svc->srv_lock, flags); rc = ptlrpc_lprocfs_svc_req_history_seek(svc, srhi, *pos); spin_unlock_irqrestore(&svc->srv_lock, flags); - + if (rc == 0) { *pos = srhi->srhi_seq; return srhi; } - + OBD_FREE(srhi, sizeof(*srhi)); return NULL; } @@ -294,7 +289,7 @@ ptlrpc_lprocfs_svc_req_history_stop(struct seq_file *s, void *iter) } static void * -ptlrpc_lprocfs_svc_req_history_next(struct seq_file *s, +ptlrpc_lprocfs_svc_req_history_next(struct seq_file *s, void *iter, loff_t *pos) { struct ptlrpc_service *svc = s->private; @@ -378,13 +373,13 @@ void ptlrpc_lprocfs_register_service(struct proc_dir_entry *entry, struct ptlrpc_service *svc) { struct lprocfs_vars lproc_vars[] = { - {.name = "req_buffer_history_len", + {.name = "req_buffer_history_len", .write_fptr = NULL, - .read_fptr = ptlrpc_lprocfs_read_req_history_len, + .read_fptr = ptlrpc_lprocfs_read_req_history_len, .data = svc}, {.name = "req_buffer_history_max", .write_fptr = ptlrpc_lprocfs_write_req_history_max, - .read_fptr = ptlrpc_lprocfs_read_req_history_max, + .read_fptr = ptlrpc_lprocfs_read_req_history_max, .data = svc}, {NULL} }; @@ -400,13 +395,13 @@ void ptlrpc_lprocfs_register_service(struct proc_dir_entry *entry, ptlrpc_lprocfs_register(entry, svc->srv_name, "stats", &svc->srv_procroot, &svc->srv_stats); - + if (svc->srv_procroot == NULL) return; lprocfs_add_vars(svc->srv_procroot, lproc_vars, NULL); - req_history = create_proc_entry("req_history", 0400, + req_history = create_proc_entry("req_history", 0400, svc->srv_procroot); if (req_history != NULL) { req_history->data = svc; @@ -420,6 +415,7 @@ void ptlrpc_lprocfs_register_obd(struct obd_device *obddev) &obddev->obd_svc_procroot, &obddev->obd_svc_stats); } +EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd); void ptlrpc_lprocfs_rpc_sent(struct ptlrpc_request *req) { @@ -456,6 +452,8 @@ void ptlrpc_lprocfs_unregister_obd(struct obd_device *obd) obd->obd_svc_stats = NULL; } } +EXPORT_SYMBOL(ptlrpc_lprocfs_unregister_obd); + int lprocfs_wr_evict_client(struct file *file, const char *buffer, unsigned long count, void *data) @@ -484,7 +482,7 @@ int lprocfs_wr_evict_client(struct file *file, const char *buffer, CERROR("can't disconnect %s: no export found\n", doomed.uuid); } else { CERROR("evicting %s at adminstrative request\n", doomed.uuid); - ptlrpc_fail_export(doomed_exp); + class_fail_export(doomed_exp); class_export_put(doomed_exp); } return count; diff --git a/lustre/ptlrpc/niobuf.c b/lustre/ptlrpc/niobuf.c index 9be4e92..0928174 100644 --- a/lustre/ptlrpc/niobuf.c +++ b/lustre/ptlrpc/niobuf.c @@ -219,7 +219,9 @@ int ptlrpc_register_bulk (struct ptlrpc_request *req) /* XXX Registering the same xid on retried bulk makes my head * explode trying to understand how the original request's bulk * might interfere with the retried request -eeb */ - LASSERT (!desc->bd_registered || req->rq_xid != desc->bd_last_xid); + LASSERTF (!desc->bd_registered || req->rq_xid != desc->bd_last_xid, + "registered: %d rq_xid: "LPU64" bd_last_xid: "LPU64"\n", + desc->bd_registered, req->rq_xid, desc->bd_last_xid); desc->bd_registered = 1; desc->bd_last_xid = req->rq_xid; @@ -381,6 +383,8 @@ int ptl_send_rpc(struct ptlrpc_request *request) ptl_md_t reply_md; ENTRY; + OBD_FAIL_RETURN(OBD_FAIL_PTLRPC_DROP_RPC, 0); + LASSERT (request->rq_type == PTL_RPC_MSG_REQUEST); /* If this is a re-transmit, we're required to have disengaged @@ -395,7 +399,7 @@ int ptl_send_rpc(struct ptlrpc_request *request) request->rq_err = 1; RETURN(-ENODEV); } - + connection = request->rq_import->imp_connection; if (request->rq_bulk != NULL) { diff --git a/lustre/ptlrpc/pack_generic.c b/lustre/ptlrpc/pack_generic.c index c571678..ef1aa0e 100644 --- a/lustre/ptlrpc/pack_generic.c +++ b/lustre/ptlrpc/pack_generic.c @@ -753,42 +753,6 @@ int llog_log_swabbed(struct llog_log_hdr *hdr) return -1; } -void lustre_swab_llogd_body (struct llogd_body *d) -{ - __swab64s (&d->lgd_logid.lgl_oid); - __swab64s (&d->lgd_logid.lgl_ogr); - __swab32s (&d->lgd_logid.lgl_ogen); - __swab32s (&d->lgd_ctxt_idx); - __swab32s (&d->lgd_llh_flags); - __swab32s (&d->lgd_index); - __swab32s (&d->lgd_saved_index); - __swab32s (&d->lgd_len); - __swab64s (&d->lgd_cur_offset); -} - -void lustre_swab_llog_hdr (struct llog_log_hdr *h) -{ - __swab32s (&h->llh_hdr.lrh_index); - __swab32s (&h->llh_hdr.lrh_len); - __swab32s (&h->llh_hdr.lrh_type); - __swab64s (&h->llh_timestamp); - __swab32s (&h->llh_count); - __swab32s (&h->llh_bitmap_offset); - __swab32s (&h->llh_flags); - __swab32s (&h->llh_tail.lrt_index); - __swab32s (&h->llh_tail.lrt_len); -} - -void lustre_swab_llogd_conn_body (struct llogd_conn_body *d) -{ - __swab64s (&d->lgdc_gen.mnt_cnt); - __swab64s (&d->lgdc_gen.conn_cnt); - __swab64s (&d->lgdc_logid.lgl_oid); - __swab64s (&d->lgdc_logid.lgl_ogr); - __swab32s (&d->lgdc_logid.lgl_ogen); - __swab32s (&d->lgdc_ctxt_idx); -} - void lustre_swab_qdata(struct qunit_data *d) { __swab32s (&d->qd_id); @@ -1883,7 +1847,7 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct ldlm_res_id *)0)->name[4])); /* Checks for struct ldlm_extent */ - LASSERTF((int)sizeof(struct ldlm_extent) == 16, " found %lld\n", + LASSERTF((int)sizeof(struct ldlm_extent) == 24, " found %lld\n", (long long)(int)sizeof(struct ldlm_extent)); LASSERTF((int)offsetof(struct ldlm_extent, start) == 0, " found %lld\n", (long long)(int)offsetof(struct ldlm_extent, start)); diff --git a/lustre/ptlrpc/pers.c b/lustre/ptlrpc/pers.c index 6f5d086..2bd04f2 100644 --- a/lustre/ptlrpc/pers.c +++ b/lustre/ptlrpc/pers.c @@ -93,7 +93,13 @@ void ptlrpc_fill_bulk_md(ptl_md_t *md, struct ptlrpc_bulk_desc *desc) { #if CRAY_PORTALS LASSERT (!(md->options & (PTL_MD_IOVEC | PTL_MD_PHYS))); +#if defined(REDSTORM) && (NALID_FROM_IFACE(CRAY_QK_NAL) == PTL_IFACE_SS_ACCEL) + /* Enforce iov_count == 1 constraint only for SeaStar accel mode on + * compute nodes (ie, REDSTORM) + * + * iov_count of > 1 is supported via PTL_MD_IOVEC in other contexts */ LASSERT (desc->bd_iov_count == 1); +#endif #else LASSERT (!(md->options & (PTL_MD_IOVEC | PTL_MD_KIOV | PTL_MD_PHYS))); #endif diff --git a/lustre/ptlrpc/pinger.c b/lustre/ptlrpc/pinger.c index 05172e2..8b75dcf 100644 --- a/lustre/ptlrpc/pinger.c +++ b/lustre/ptlrpc/pinger.c @@ -38,7 +38,7 @@ static DECLARE_MUTEX(pinger_sem); static struct list_head pinger_imports = LIST_HEAD_INIT(pinger_imports); -int ptlrpc_ping(struct obd_import *imp) +int ptlrpc_ping(struct obd_import *imp) { struct ptlrpc_request *req; int rc = 0; @@ -47,17 +47,16 @@ int ptlrpc_ping(struct obd_import *imp) req = ptlrpc_prep_req(imp, OBD_PING, 0, NULL, NULL); if (req) { - DEBUG_REQ(D_HA, req, "pinging %s->%s", + DEBUG_REQ(D_INFO, req, "pinging %s->%s", imp->imp_obd->obd_uuid.uuid, imp->imp_target_uuid.uuid); req->rq_no_resend = req->rq_no_delay = 1; - req->rq_replen = lustre_msg_size(0, - NULL); + req->rq_replen = lustre_msg_size(0, NULL); ptlrpcd_add_req(req); } else { CERROR("OOM trying to ping %s->%s\n", - imp->imp_obd->obd_uuid.uuid, - imp->imp_target_uuid.uuid); + imp->imp_obd->obd_uuid.uuid, + imp->imp_target_uuid.uuid); rc = -ENOMEM; } @@ -66,7 +65,13 @@ int ptlrpc_ping(struct obd_import *imp) static inline void ptlrpc_update_next_ping(struct obd_import *imp) { - imp->imp_next_ping = jiffies + obd_timeout * HZ; + imp->imp_next_ping = jiffies + HZ * + (imp->imp_state == LUSTRE_IMP_DISCON ? 10 : PING_INTERVAL); +} + +void ptlrpc_ping_import_soon(struct obd_import *imp) +{ + imp->imp_next_ping = jiffies; } #ifdef __KERNEL__ @@ -99,7 +104,7 @@ static int ptlrpc_pinger_main(void *arg) while (1) { unsigned long this_ping = jiffies; long time_to_next_ping; - struct l_wait_info lwi = LWI_TIMEOUT(obd_timeout * HZ, + struct l_wait_info lwi = LWI_TIMEOUT(PING_INTERVAL * HZ, NULL, NULL); struct list_head *iter; @@ -115,52 +120,66 @@ static int ptlrpc_pinger_main(void *arg) spin_lock_irqsave(&imp->imp_lock, flags); level = imp->imp_state; force = imp->imp_force_verify; - if (force) - imp->imp_force_verify = 0; + imp->imp_force_verify = 0; spin_unlock_irqrestore(&imp->imp_lock, flags); + CDEBUG(level == LUSTRE_IMP_FULL ? D_INFO : D_HA, + "level %s/%u force %u deactive %u pingable %u\n", + ptlrpc_import_state_name(level), level, + force, imp->imp_deactive, imp->imp_pingable); if (force || - time_after_eq(this_ping, imp->imp_next_ping)) { + /* if the next ping is within, say, 5 jiffies from + now, go ahead and ping. See note below. */ + time_after_eq(this_ping, imp->imp_next_ping - 5)) { if (level == LUSTRE_IMP_DISCON && !imp->imp_deactive) { /* wait at least a timeout before trying recovery again. */ - ptlrpc_update_next_ping(imp); + imp->imp_next_ping = jiffies + + obd_timeout * HZ; ptlrpc_initiate_recovery(imp); - } - else if (level != LUSTRE_IMP_FULL || - imp->imp_obd->obd_no_recov) { - CDEBUG(D_HA, - "not pinging %s (in recovery " - "or recovery disabled: %s)\n", + } else if (level != LUSTRE_IMP_FULL || + imp->imp_obd->obd_no_recov || + imp->imp_deactive) { + CDEBUG(D_HA, "not pinging %s " + "(in recovery: %s or recovery " + "disabled: %u/%u)\n", imp->imp_target_uuid.uuid, - ptlrpc_import_state_name(level)); - } - else if (imp->imp_pingable || force) { + ptlrpc_import_state_name(level), + imp->imp_deactive, + imp->imp_obd->obd_no_recov); + } else if (imp->imp_pingable || force) { ptlrpc_ping(imp); } - - } else if (!imp->imp_pingable) { - continue; + } else { + if (!imp->imp_pingable) + continue; + CDEBUG(D_INFO, + "don't need to ping %s (%lu > %lu)\n", + imp->imp_target_uuid.uuid, + imp->imp_next_ping, this_ping); } - CDEBUG(D_HA, "don't need to ping %s (%lu > %lu)\n", - imp->imp_target_uuid.uuid, - imp->imp_next_ping, this_ping); - /* obd_timeout might have changed */ if (time_after(imp->imp_next_ping, - this_ping + obd_timeout * HZ)) + this_ping + PING_INTERVAL * HZ)) ptlrpc_update_next_ping(imp); } up(&pinger_sem); /* Wait until the next ping time, or until we're stopped. */ - time_to_next_ping = this_ping + (obd_timeout * HZ) - jiffies; - CDEBUG(D_HA, "next ping in %lu (%lu)\n", time_to_next_ping, - this_ping + obd_timeout * HZ); + time_to_next_ping = this_ping + (PING_INTERVAL * HZ) - jiffies; + /* The ping sent by ptlrpc_send_rpc may get sent out + say .01 second after this. + ptlrpc_pinger_sending_on_import will then set the + next ping time to next_ping + .01 sec, which means + we will SKIP the next ping at next_ping, and the + ping will get sent 2 timeouts from now! Beware. */ + CDEBUG(D_INFO, "next ping in %lu (%lu)\n", time_to_next_ping, + this_ping + PING_INTERVAL * HZ); if (time_to_next_ping > 0) { - lwi = LWI_TIMEOUT(time_to_next_ping, NULL, NULL); + lwi = LWI_TIMEOUT(max_t(long, time_to_next_ping, HZ), + NULL, NULL); l_wait_event(thread->t_ctl_waitq, thread->t_flags & (SVC_STOPPING|SVC_EVENT), &lwi); @@ -336,6 +355,8 @@ static int pinger_check_rpcs(void *arg) pd->pd_this_ping = curtime; pd->pd_set = ptlrpc_prep_set(); + if (pd->pd_set == NULL) + goto out; set = pd->pd_set; /* add rpcs into set */ @@ -346,7 +367,7 @@ static int pinger_check_rpcs(void *arg) int generation, level; unsigned long flags; - if (time_after_eq(pd->pd_this_ping, imp->imp_next_ping)) { + if (time_after_eq(pd->pd_this_ping, imp->imp_next_ping - 5)) { /* Add a ping. */ spin_lock_irqsave(&imp->imp_lock, flags); generation = imp->imp_generation; @@ -399,7 +420,7 @@ do_check_set: rc = ptlrpc_check_set(set); /* not finished, and we are not expired, simply return */ - if (!rc && time_before(curtime, pd->pd_this_ping + obd_timeout * HZ)) { + if (!rc && time_before(curtime, pd->pd_this_ping + PING_INTERVAL * HZ)) { CDEBUG(D_HA, "not finished, but also not expired\n"); pd->pd_recursion--; return 0; @@ -430,7 +451,8 @@ do_check_set: ptlrpc_set_destroy(set); pd->pd_set = NULL; - pd->pd_next_ping = pd->pd_this_ping + obd_timeout * HZ; +out: + pd->pd_next_ping = pd->pd_this_ping + PING_INTERVAL * HZ; pd->pd_this_ping = 0; /* XXX for debug */ CDEBUG(D_HA, "finished a round ping\n"); diff --git a/lustre/ptlrpc/ptlrpc_internal.h b/lustre/ptlrpc/ptlrpc_internal.h index e49b5f9..70dcbf2 100644 --- a/lustre/ptlrpc/ptlrpc_internal.h +++ b/lustre/ptlrpc/ptlrpc_internal.h @@ -41,7 +41,7 @@ int ptlrpc_replay_next(struct obd_import *imp, int *inflight); void ptlrpc_initiate_recovery(struct obd_import *imp); -#ifdef __KERNEL__ +#ifdef LPROCFS void ptlrpc_lprocfs_register_service(struct proc_dir_entry *proc_entry, struct ptlrpc_service *svc); void ptlrpc_lprocfs_unregister_service(struct ptlrpc_service *svc); @@ -117,5 +117,6 @@ int ptlrpc_start_pinger(void); int ptlrpc_stop_pinger(void); void ptlrpc_pinger_sending_on_import(struct obd_import *imp); void ptlrpc_pinger_wake_up(void); +void ptlrpc_ping_import_soon(struct obd_import *imp); #endif /* PTLRPC_INTERNAL_H */ diff --git a/lustre/ptlrpc/ptlrpc_module.c b/lustre/ptlrpc/ptlrpc_module.c index 4629b71..f9c4bdb 100644 --- a/lustre/ptlrpc/ptlrpc_module.c +++ b/lustre/ptlrpc/ptlrpc_module.c @@ -189,7 +189,6 @@ EXPORT_SYMBOL(ptlrpc_activate_import); EXPORT_SYMBOL(ptlrpc_deactivate_import); EXPORT_SYMBOL(ptlrpc_invalidate_import); EXPORT_SYMBOL(ptlrpc_fail_import); -EXPORT_SYMBOL(ptlrpc_fail_export); EXPORT_SYMBOL(ptlrpc_recover_import); /* pinger.c */ @@ -203,10 +202,6 @@ EXPORT_SYMBOL(ptlrpcd_decref); EXPORT_SYMBOL(ptlrpcd_add_req); EXPORT_SYMBOL(ptlrpcd_wake); -/* lproc_ptlrpc.c */ -EXPORT_SYMBOL(ptlrpc_lprocfs_register_obd); -EXPORT_SYMBOL(ptlrpc_lprocfs_unregister_obd); - /* llogd.c */ EXPORT_SYMBOL(llog_origin_handle_create); EXPORT_SYMBOL(llog_origin_handle_next_block); diff --git a/lustre/ptlrpc/recov_thread.c b/lustre/ptlrpc/recov_thread.c index 6d0f118..3a78b18 100644 --- a/lustre/ptlrpc/recov_thread.c +++ b/lustre/ptlrpc/recov_thread.c @@ -272,6 +272,7 @@ static int log_commit_thread(void *arg) sending_list = &lcm->lcm_llcd_pending; resend: + import = NULL; if (lcm->lcm_flags & LLOG_LCM_FL_EXIT) { lcm->lcm_llcd_maxfree = 0; lcm->lcm_llcd_minfree = 0; @@ -340,6 +341,13 @@ static int log_commit_thread(void *arg) } up(&llcd->llcd_ctxt->loc_sem); + if (!import || (import == LP_POISON)) { + CERROR("No import %p (llcd=%p, ctxt=%p)\n", + import, llcd, llcd->llcd_ctxt); + llcd_put(llcd); + continue; + } + request = ptlrpc_prep_req(import, OBD_LOG_CANCEL, 1, &llcd->llcd_cookiebytes, bufs); diff --git a/lustre/ptlrpc/recover.c b/lustre/ptlrpc/recover.c index a5c9e21..e79e567 100644 --- a/lustre/ptlrpc/recover.c +++ b/lustre/ptlrpc/recover.c @@ -71,7 +71,7 @@ void ptlrpc_run_recovery_over_upcall(struct obd_device *obd) } else { CWARN("Invoked upcall %s %s %s\n", - argv[0], argv[1], argv[2]); + argv[0], argv[1], argv[2]); } } @@ -91,7 +91,7 @@ void ptlrpc_run_failed_import_upcall(struct obd_import* imp) return; } spin_unlock_irqrestore(&imp->imp_lock, flags); - + argv[0] = obd_lustre_upcall; argv[1] = "FAILED_IMPORT"; argv[2] = imp->imp_target_uuid.uuid; @@ -108,8 +108,7 @@ void ptlrpc_run_failed_import_upcall(struct obd_import* imp) if (rc < 0) { CERROR("Error invoking recovery upcall %s %s %s %s %s %s: %d; " "check /proc/sys/lustre/lustre_upcall\n", - argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], rc); - + argv[0], argv[1], argv[2], argv[3], argv[4], argv[5],rc); } else { CWARN("Invoked upcall %s %s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); @@ -130,17 +129,15 @@ void ptlrpc_initiate_recovery(struct obd_import *imp) ENTRY; LASSERT (obd_lustre_upcall != NULL); - + if (strcmp(obd_lustre_upcall, "DEFAULT") == 0) { CDEBUG(D_HA, "%s: starting recovery without upcall\n", imp->imp_target_uuid.uuid); ptlrpc_connect_import(imp, NULL); - } - else if (strcmp(obd_lustre_upcall, "NONE") == 0) { + } else if (strcmp(obd_lustre_upcall, "NONE") == 0) { CDEBUG(D_HA, "%s: recovery disabled\n", imp->imp_target_uuid.uuid); - } - else { + } else { CDEBUG(D_HA, "%s: calling upcall to start recovery\n", imp->imp_target_uuid.uuid); ptlrpc_run_failed_import_upcall(imp); @@ -271,7 +268,6 @@ void ptlrpc_wake_delayed(struct obd_import *imp) void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req) { - int rc; struct obd_import *imp= failed_req->rq_import; unsigned long flags; ENTRY; @@ -290,8 +286,7 @@ void ptlrpc_request_handle_notconn(struct ptlrpc_request *failed_req) imp->imp_obd->obd_name); ptlrpc_deactivate_import(imp); } - - rc = ptlrpc_connect_import(imp, NULL); + ptlrpc_connect_import(imp, NULL); } /* Wait for recovery to complete and resend. If evicted, then @@ -318,9 +313,11 @@ int ptlrpc_set_import_active(struct obd_import *imp, int active) /* When deactivating, mark import invalid, and abort in-flight * requests. */ if (!active) { + CWARN("setting import %s INACTIVE by administrator request\n", + imp->imp_target_uuid.uuid); ptlrpc_invalidate_import(imp); imp->imp_deactive = 1; - } + } /* When activating, mark import valid, and attempt recovery */ if (active) { @@ -341,6 +338,7 @@ int ptlrpc_recover_import(struct obd_import *imp, char *new_uuid) /* force import to be disconnected. */ ptlrpc_set_import_discon(imp); + imp->imp_deactive = 0; rc = ptlrpc_recover_import_no_retry(imp, new_uuid); RETURN(rc); @@ -392,34 +390,3 @@ static int ptlrpc_recover_import_no_retry(struct obd_import *imp, RETURN(rc); } - -void ptlrpc_fail_export(struct obd_export *exp) -{ - int rc, already_failed; - unsigned long flags; - - spin_lock_irqsave(&exp->exp_lock, flags); - already_failed = exp->exp_failed; - exp->exp_failed = 1; - spin_unlock_irqrestore(&exp->exp_lock, flags); - - if (already_failed) { - CDEBUG(D_HA, "disconnecting dead export %p/%s; skipping\n", - exp, exp->exp_client_uuid.uuid); - return; - } - - CDEBUG(D_HA, "disconnecting export %p/%s\n", - exp, exp->exp_client_uuid.uuid); - - if (obd_dump_on_timeout) - portals_debug_dumplog(); - - /* Most callers into obd_disconnect are removing their own reference - * (request, for example) in addition to the one from the hash table. - * We don't have such a reference here, so make one. */ - class_export_get(exp); - rc = obd_disconnect(exp); - if (rc) - CERROR("disconnecting export %p failed: %d\n", exp, rc); -} diff --git a/lustre/ptlrpc/service.c b/lustre/ptlrpc/service.c index 9b68cf3..321ac50 100644 --- a/lustre/ptlrpc/service.c +++ b/lustre/ptlrpc/service.c @@ -417,6 +417,8 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc) int rc; ENTRY; + LASSERT(svc); + spin_lock_irqsave (&svc->srv_lock, flags); if (list_empty (&svc->srv_request_queue) || (svc->srv_n_difficult_replies != 0 && @@ -469,17 +471,6 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc) CDEBUG(D_NET, "got req "LPD64"\n", request->rq_xid); - /* Discard requests queued for longer than my timeout. If the - * client's timeout is similar to mine, she'll be timing out this - * REQ anyway (bug 1502) */ - if (timediff / 1000000 > (long)obd_timeout) { - CERROR("Dropping timed-out opc %d request from %s" - ": %ld seconds old\n", request->rq_reqmsg->opc, - libcfs_id2str(request->rq_peer), - timediff / 1000000); - goto out; - } - request->rq_export = class_conn2export(&request->rq_reqmsg->handle); if (request->rq_export) { @@ -502,7 +493,19 @@ ptlrpc_server_handle_request (struct ptlrpc_service *svc) goto put_conn; } - request->rq_export->exp_last_request_time = CURRENT_SECONDS; + class_update_export_timer(request->rq_export, + (time_t)(timediff / 1000000)); + } + + /* Discard requests queued for longer than my timeout. If the + * client's timeout is similar to mine, she'll be timing out this + * REQ anyway (bug 1502) */ + if (timediff / 1000000 > (long)obd_timeout) { + CERROR("Dropping timed-out opc %d request from %s" + ": %ld seconds old\n", request->rq_reqmsg->opc, + libcfs_id2str(request->rq_peer), + timediff / 1000000); + goto put_conn; } request->rq_phase = RQ_PHASE_INTERPRET; @@ -695,6 +698,7 @@ liblustre_check_services (void *arg) RETURN(did_something); } +#define ptlrpc_stop_all_threads(s) do {} while (0) #else /* __KERNEL__ */ diff --git a/lustre/tests/.RC_CURRENT.tag b/lustre/tests/.RC_CURRENT.tag index 7382334..1b1bf07 100644 --- a/lustre/tests/.RC_CURRENT.tag +++ b/lustre/tests/.RC_CURRENT.tag @@ -1 +1 @@ -RC_1_3_0_19 +RC_1_3_0_30 diff --git a/lustre/tests/.cvsignore b/lustre/tests/.cvsignore index c39784f..0b2290a 100644 --- a/lustre/tests/.cvsignore +++ b/lustre/tests/.cvsignore @@ -68,4 +68,5 @@ rename_many memhog mmap_sanity rmdirmany +flock_test writemany diff --git a/lustre/tests/2ost.sh b/lustre/tests/2ost.sh new file mode 100644 index 0000000..1f890fb --- /dev/null +++ b/lustre/tests/2ost.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +export PATH=`dirname $0`/../utils:$PATH + +config=${1:-`basename $0 .sh`.xml} + +LMC="${LMC:-lmc} -m $config" +TMP=${TMP:-/tmp} + +MDSDEV=${MDSDEV:-$TMP/mds1-`hostname`} +MDSSIZE=${MDSSIZE:-400000} +FSTYPE=${FSTYPE:-ext3} +MOUNT=${MOUNT:-/mnt/lustre} +MOUNT2=${MOUNT2:-${MOUNT}2} +NETTYPE=${NETTYPE:-tcp} + +OSTDEV=${OSTDEV:-$TMP/ost-`hostname`} +OSTSIZE=${OSTSIZE:-400000} + +# specific journal size for the ost, in MB +JSIZE=${JSIZE:-0} +[ "$JSIZE" -gt 0 ] && JARG="--journal_size $JSIZE" +MDSISIZE=${MDSISIZE:-0} +[ "$MDSISIZE" -gt 0 ] && IARG="--inode_size $MDSISIZE" + +STRIPE_BYTES=${STRIPE_BYTES:-1048576} +STRIPES_PER_OBJ=0 # 0 means stripe over all OSTs + +rm -f $config + +# create nodes +${LMC} --add node --node localhost || exit 10 +${LMC} --add net --node localhost --nid `hostname` --nettype $NETTYPE || exit 11 +${LMC} --add net --node client --nid '*' --nettype $NETTYPE || exit 12 + +# configure mds server +${LMC} --add mds --node localhost --mds mds1 --fstype $FSTYPE \ + --dev $MDSDEV --size $MDSSIZE $JARG $IARG $MDSOPT || exit 20 + +# configure ost +${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz $STRIPE_BYTES \ + --stripe_cnt $STRIPES_PER_OBJ --stripe_pattern 0 $LOVOPT || exit 20 +${LMC} --add ost --ost ost1 --node localhost --lov lov1 \ + --fstype $FSTYPE --dev $OSTDEV --size $OSTSIZE $JARG $OSTOPT || exit 30 +${LMC} --add ost --ost ost2 --node localhost --lov lov1 \ + --fstype $FSTYPE --dev ${OSTDEV}2 --size $OSTSIZE $JARG $OSTOPT || exit 30 + +# create client config +${LMC} --add mtpt --node localhost --path $MOUNT --mds mds1 --lov lov1 \ + $CLIENTOPT || exit 40 +${LMC} --add mtpt --node client --path $MOUNT2 --mds mds1 --lov lov1 \ + $CLIENTOPT || exit 41 diff --git a/lustre/tests/acceptance-small.sh b/lustre/tests/acceptance-small.sh index a34db0b..280f81d 100755 --- a/lustre/tests/acceptance-small.sh +++ b/lustre/tests/acceptance-small.sh @@ -24,6 +24,8 @@ fi [ "$DEBUG_OFF" ] || DEBUG_OFF="eval echo $DEBUG_LVL > /proc/sys/portals/debug" [ "$DEBUG_ON" ] || DEBUG_ON="eval echo -1 > /proc/sys/portals/debug" +LIBLUSTRETESTS=${LIBLUSTRETESTS:-../liblustre/tests} + for NAME in $CONFIGS; do export NAME MOUNT START CLEAN [ -e $NAME.sh ] && sh $NAME.sh @@ -40,7 +42,7 @@ for NAME in $CONFIGS; do if [ "$DBENCH" != "no" ]; then mount | grep $MOUNT || sh llmount.sh - SPACE=`df $MOUNT | tail -n 1 | awk '{ print $4 }'` + SPACE=`df -P $MOUNT | tail -n 1 | awk '{ print $4 }'` DB_THREADS=`expr $SPACE / 50000` [ $THREADS -lt $DB_THREADS ] && DB_THREADS=$THREADS @@ -69,44 +71,45 @@ for NAME in $CONFIGS; do fi IOZONE_OPTS="-i 0 -i 1 -i 2 -+d -r $RSIZE -s $SIZE" if [ "$O_DIRECT" -a "$O_DIRECT" != "no" ]; then - IOZONE_OPTS="-I $IOZONE_OPTS" + IOZONE_OPTS="-I $IOZONE_OPTS" fi - IOZONE_FILE="-f $MOUNT/iozone" + IOZFILE="-f $MOUNT/iozone" if [ "$IOZONE" != "no" ]; then mount | grep $MOUNT || sh llmount.sh $DEBUG_OFF - iozone $IOZONE_OPTS $IOZONE_FILE + iozone $IOZONE_OPTS $IOZFILE $DEBUG_ON sh llmountcleanup.sh sh llrmount.sh - fi - if [ "$IOZONE_DIR" != "no" ]; then - mount | grep $MOUNT || sh llmount.sh - SPACE=`df $MOUNT | tail -n 1 | awk '{ print $4 }'` - IOZ_THREADS=`expr $SPACE / \( $SIZE + $SIZE / 512 \)` - [ $THREADS -lt $IOZ_THREADS ] && IOZ_THREADS=$THREADS - $DEBUG_OFF - iozone $IOZONE_OPTS $IOZONE_FILE.odir - IOZVER=`iozone -v | awk '/Revision:/ { print $3 }' | tr -d '.'` - $DEBUG_ON - sh llmountcleanup.sh - sh llrmount.sh - if [ "$IOZ_THREADS" -gt 1 -a "$IOZVER" -ge 3145 ]; then + if [ "$IOZONE_DIR" != "no" ]; then + mount | grep $MOUNT || sh llmount.sh + SPACE=`df -P $MOUNT | tail -n 1 | awk '{ print $4 }'` + IOZ_THREADS=`expr $SPACE / \( $SIZE + $SIZE / 512 \)` + [ $THREADS -lt $IOZ_THREADS ] && IOZ_THREADS=$THREADS + $DEBUG_OFF - THREAD=1 - IOZONE_FILE="-F " - while [ $THREAD -le $IOZ_THREADS ]; do - IOZONE_FILE="$IOZONE_FILE $MOUNT/iozone.$THREAD" - THREAD=`expr $THREAD + 1` - done - iozone $IOZONE_OPTS -t $IOZ_THREADS $IOZONE_FILE + iozone $IOZONE_OPTS $IOZFILE.odir + IOZVER=`iozone -v|awk '/Revision:/ {print $3}'|tr -d .` $DEBUG_ON sh llmountcleanup.sh sh llrmount.sh - elif [ $IOZVER -lt 3145 ]; then - VER=`iozone -v | awk '/Revision:/ { print $3 }'` - echo "iozone $VER too old for multi-threaded tests" + if [ "$IOZ_THREADS" -gt 1 -a "$IOZVER" -ge 3145 ]; then + $DEBUG_OFF + THREAD=1 + IOZFILE="-F " + while [ $THREAD -le $IOZ_THREADS ]; do + IOZFILE="$IOZFILE $MOUNT/iozone.$THREAD" + THREAD=`expr $THREAD + 1` + done + iozone $IOZONE_OPTS -t $IOZ_THREADS $IOZFILE + $DEBUG_ON + sh llmountcleanup.sh + sh llrmount.sh + elif [ $IOZVER -lt 3145 ]; then + VER=`iozone -v | awk '/Revision:/ { print $3 }'` + echo "iozone $VER too old for multi-thread test" + fi fi fi if [ "$FSX" != "no" ]; then @@ -118,20 +121,22 @@ for NAME in $CONFIGS; do sh llmountcleanup.sh sh llrmount.sh fi + + mkdir -p $MOUNT2 + case $NAME in + local|lov) + MDSNODE=`hostname` + MDSNAME=mds1 + CLIENT=client + ;; + *) # we could extract this from $NAME.xml somehow + ;; + esac + if [ "$SANITYN" != "no" ]; then mount | grep $MOUNT || sh llmount.sh $DEBUG_OFF - mkdir -p $MOUNT2 - case $NAME in - local|lov) - MDSNODE=`hostname` - MDSNAME=mds1 - CLIENT=client - ;; - *) # we could extract this from $NAME.xml somehow - ;; - esac if [ "$MDSNODE" -a "$MDSNAME" -a "$CLIENT" ]; then llmount $MDSNODE:/$MDSNAME/$CLIENT $MOUNT2 SANITYLOG=$TMP/sanity.log START=: CLEAN=: sh sanityN.sh @@ -143,6 +148,18 @@ for NAME in $CONFIGS; do $DEBUG_ON sh llmountcleanup.sh + sh llrmount.sh + fi + + if [ "$LIBLUSTRE" != "no" ]; then + mount | grep $MOUNT || sh llmount.sh + IPADDR=`ping -c 1 $MDSNODE|head -n 1|sed -e "s/[^(]*(//" -e "s/).*//"` + export ENV_LUSTRE_MNTPNT=$MOUNT2 + export ENV_LUSTRE_MNTTGT=$IPADDR:/$MDSNAME/$CLIENT + if [ -x $LIBLUSTRETESTS/sanity ]; then + $LIBLUSTRETESTS/sanity --target=$ENV_LUSTRE_MNTTGT + fi + sh llmountcleanup.sh #sh llrmount.sh fi diff --git a/lustre/tests/cfg/local.sh b/lustre/tests/cfg/local.sh index 71ff93b..924587a 100644 --- a/lustre/tests/cfg/local.sh +++ b/lustre/tests/cfg/local.sh @@ -20,9 +20,9 @@ SUBSYSTEM=${SUBSYSTEM:- 0xffb7e3ff} PDSH=${PDSH:-no_dsh} MDSDEV=${MDSDEV:-$ROOT/tmp/mds1-`hostname`} -MDSSIZE=${MDSSIZE:-10000} +MDSSIZE=${MDSSIZE:-100000} OSTDEV=${OSTDEV:-$ROOT/tmp/ost1-`hostname`} -OSTSIZE=${OSTSIZE:-50000} +OSTSIZE=${OSTSIZE:-200000} FSTYPE=${FSTYPE:-ext3} TIMEOUT=${TIMEOUT:-20} UPCALL=${UPCALL:-DEFAULT} diff --git a/lustre/tests/cfg/mdev.sh b/lustre/tests/cfg/mdev.sh index 38031e1..c7f7674 100644 --- a/lustre/tests/cfg/mdev.sh +++ b/lustre/tests/cfg/mdev.sh @@ -16,9 +16,9 @@ SUBSYSTEM=${SUBSYSTEM:- 0xffb7e3ff} PDSH=${PDSH:-pdsh -S -w} MDSDEV=${MDSDEV:-/dev/sda1} -MDSSIZE=${MDSSIZE:-50000} +MDSSIZE=${MDSSIZE:-100000} OSTDEV=${OSTDEV:-/tmp/ost1-`hostname`} -OSTSIZE=${OSTSIZE:-20000} +OSTSIZE=${OSTSIZE:-200000} FSTYPE=${FSTYPE:-ext3} TIMEOUT=${TIMEOUT:-10} #UPCALL=${UPCALL:-$PWD/replay-single-upcall.sh} diff --git a/lustre/tests/echo.sh b/lustre/tests/echo.sh index b937c17..1d90308 100755 --- a/lustre/tests/echo.sh +++ b/lustre/tests/echo.sh @@ -31,6 +31,13 @@ h2gm () { h2elan () { echo $1 | sed 's/[^0-9]*//g' } + +h2iib () { + case $1 in + client) echo '\*' ;; + *) echo $1 | sed "s/[^0-9]*//" ;; + esac +} # FIXME: make LMC not require MDS for obdecho LOV MDSDEV=${MDSDEV:-$TMP/mds1-`hostname`} diff --git a/lustre/tests/llmount.sh b/lustre/tests/llmount.sh index 49a962b..90ef09d 100755 --- a/lustre/tests/llmount.sh +++ b/lustre/tests/llmount.sh @@ -26,12 +26,11 @@ else fi [ "$NODE" ] && node_opt="--node $NODE" +[ "$DEBUG" ] && debug_opt="--ptldebug=$DEBUG" -${LCONF} $NOMOD $portals_opt $lustre_opt $node_opt ${REFORMAT:---reformat} $@ \ +${LCONF} $NOMOD $portals_opt $lustre_opt $debug_opt $node_opt ${REFORMAT:---reformat} $@ \ $conf_opt || exit 2 -[ $DEBUG ] && sysctl -w portals.debug=$DEBUG - if [ "$MOUNT2" ]; then $LLMOUNT -v `hostname`:/mds1/client $MOUNT2 || exit 3 fi diff --git a/lustre/tests/lov.sh b/lustre/tests/lov.sh index 4783833..afbbfad 100755 --- a/lustre/tests/lov.sh +++ b/lustre/tests/lov.sh @@ -16,7 +16,7 @@ MOUNT=${MOUNT:-/mnt/lustre} MOUNT2=${MOUNT2:-${MOUNT}2} NETTYPE=${NETTYPE:-tcp} -OSTCOUNT=${OSTCOUNT:-2} +OSTCOUNT=${OSTCOUNT:-5} # OSTDEVN will still override the device for OST N OSTSIZE=${OSTSIZE:-150000} diff --git a/lustre/tests/mcr-individual-ost-nogw-config.sh b/lustre/tests/mcr-individual-ost-nogw-config.sh deleted file mode 100755 index 0401bf5..0000000 --- a/lustre/tests/mcr-individual-ost-nogw-config.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -config=${1:-echo-no-gw.xml} - -LMC="save_cmd" -LMC_REAL="../../lustre/utils/lmc -m $config" - -# TCP/IP servers -SERVER_START=0 -SERVER_CNT=62 - -TCPBUF=1048576 - -h2tcp () { - echo "${1}" -} -BATCH=/tmp/lmc-batch.$$ -save_cmd() { - echo "$@" >> $BATCH -} - -[ -f $config ] && rm $config - -# Client node -${LMC} --add net --node client --tcpbuf $TCPBUF --nid '*' --nettype tcp || exit 1 - -# this is crude, but effective -let server_per_gw=($SERVER_CNT / $GW_CNT ) -let tot_server=$server_per_gw*$GW_CNT - -let server=$SERVER_START -while (( $server < $SERVER_CNT + SERVER_START )); -do - echo "server: $server" - OST=ba$server - # server node - ${LMC} --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp || exit 1 - # the device on the server - ${LMC} --add ost --node $OST --obd obd_$OST --obdtype=obdecho || exit 3 - # osc on client - ${LMC} --add oscref --node client --osc OSC_obd_$OST - let server=$server+1 -done - -$LMC_REAL --batch $BATCH -rm -f $BATCH diff --git a/lustre/tests/mcr-mds-failover-config.sh b/lustre/tests/mcr-mds-failover-config.sh deleted file mode 100755 index 29ec215..0000000 --- a/lustre/tests/mcr-mds-failover-config.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh - -LMC=/usr/local/cfs/lustre/utils/lmc -# LMC="echo lmc" -CONFIG=mcr-mds-failover.xml -LUSTRE_QUERY=/usr/local/cfs/lustre-failover/lustre-query -GW_NODE=mcr21 -CLIENT_ELAN=`hostname | sed s/[^0-9]*//;` -OST=${OST:-ba50} -UUIDLIST=${UUIDLIST:-/usr/local/admin/ba-ost/UUID.txt} -OST_UUID=`awk "/$OST / { print \\$3 }" $UUIDLIST` -[ "$OST_UUID" ] && OST_UUID="--ostuuid=$OST_UUID" || echo "$OST: no UUID" -MDS_DEVICE=/dev/sda3 -MDS_SIZE=500000 -TCPBUF=1048576 - -MDSNODES=`$LUSTRE_QUERY -h emcri -s id=mds -f` -ACTIVEMDS=`$LUSTRE_QUERY -h emcri -s id=mds -a` - -echo "MDS nodes: $MDSNODES, active: $ACTIVEMDS" - -h2elan () { - echo $1 | sed 's/[^0-9]*//g' -} - -h2tcp () { - echo "${1}" -} - - -# create client node -$LMC -o $CONFIG --add net --node client --nid '*' --nettype elan -$LMC -m $CONFIG --add net --router --node mcr21 --tcpbuf $TCPBUF --nid `h2tcp $GW_NODE` --nettype tcp -$LMC -m $CONFIG --add net --router --node mcr21 --nid `h2elan $GW_NODE` --nettype elan -$LMC -m $CONFIG --add route --node $GW_NODE --nettype elan --gw `h2elan $GW_NODE` --lo $CLIENT_ELAN - -# create MDS node entries -for mds in $MDSNODES; do - elanaddr=`$LUSTRE_QUERY -h emcri -s id=$mds -e` - $LMC -m $CONFIG --add net --node $mds --nid $elanaddr --nettype elan - $LMC -m $CONFIG --add mds --node $mds --mds mds_$mds --dev $MDS_DEVICE --size $MDS_SIZE -done - -# create OST node entry -$LMC -m $CONFIG --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp -$LMC -m $CONFIG --add ost --node $OST --ost ost_$OST $OST_UUID --dev bluearc -$LMC -m $CONFIG --add route --node $GW_NODE --nettype tcp --gw `h2tcp $GW_NODE` --lo $OST - -# mount -$LMC -m $CONFIG --add mtpt --node client --path /mnt/lustre --mds mds_$ACTIVEMDS --lov ost_$OST diff --git a/lustre/tests/mcr-routed-config.sh b/lustre/tests/mcr-routed-config.sh deleted file mode 100755 index 8d8a100..0000000 --- a/lustre/tests/mcr-routed-config.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -BASE=`hostname | sed "s/[i0-9]*$//"` -[ $BASE = "mcr" ] && OSTBASE=${OSTBASE:-ba} || OSTBASE=${OSTBASE:-ba-ost-} - -config=${1:-$BASE.xml} - -BATCH=/tmp/lmc-batch.$$ -save_cmd() { - echo "$@" >> $BATCH -} - -LMC="save_cmd" -LMC_REAL="../utils/lmc -m $config" - -# TCP/IP servers -SERVER_START=0 -SERVER_CNT=32 -GW_START=0 -GW_CNT=16 -MDS=${BASE}23 -UUIDLIST=${UUIDLIST:-/usr/local/admin/ba-ost/UUID.txt} - -echo "MDS: $MDS" - -# This is needed for to create route for elan network -CLIENT_LO=38 -CLIENT_HI=191 - -TCPBUF=1048576 - -h2elan () { - echo $1 | sed 's/[^0-9]*//g' -} - -h2tcp () { - echo "${1}" -} - -# map gateway NN to host NN (assumes mcr[22-25] are not gateways) -gw2node() { - [ $1 -gt 21 ] && echo $(($1 + 4)) || echo $1 -} - -[ -f $config ] && rm $config - -${LMC} --add net --node $MDS --nid `h2elan $MDS` --nettype elan || exit 1 -${LMC} --add mds --node $MDS --mds mds1 --dev /tmp/mds1 --size 100000 || exit 1 -${LMC} --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 1 --stripe_pattern 0 - -# Client node -#${LMC} --add net --node client --tcpbuf $TCPBUF --nid '*' --nettype tcp || exit 1 -${LMC} --add net --node client --nid '*' --nettype elan || exit 1 -${LMC} --add mtpt --node client --path /mnt/lustre --mds mds1 --lov lov1 - -# this is crude, but effective -let server_per_gw=($SERVER_CNT / $GW_CNT ) -let tot_server=$server_per_gw*$GW_CNT -echo "Allocating $server_per_gw OSTs per gateway." -echo "For a total of $tot_server Blue Arc OSTs" - -let gw=$GW_START -let server=$SERVER_START -while (( $gw < $GW_CNT + GW_START )); -do - gwnode=$BASE`gw2node $gw` - echo "Router: $gwnode" - ${LMC} --add net --router --node $gwnode --tcpbuf $TCPBUF --nid `h2tcp $gwnode` --nettype tcp || exit 1 - ${LMC} --add net --node $gwnode --nid `h2elan $gwnode` --nettype elan || exit 1 - ${LMC} --add route --node $gwnode --nettype elan --gw `h2elan $gwnode` --lo `h2elan $CLIENT_LO` --hi `h2elan $CLIENT_HI` || exit 2 - - let i=0 - while (( $i < $server_per_gw )); - do - OST=${OSTBASE}$server - echo "server: $OST" - OST_UUID=`awk "/$OST / { print \\$3 }" $UUIDLIST` - [ "$OST_UUID" ] && OST_UUID="--ostuuid $OST_UUID" || echo "$OST: no UUID" - # server node - ${LMC} --add net --node $OST --tcpbuf $TCPBUF --nid $OST --nettype tcp || exit 1 - # the device on the server - ${LMC} --add ost --lov lov1 --node $OST $OBD_UUID --dev bluearc || exit 3 - # route to server - ${LMC} --add route --node $gwnode --nettype tcp --gw `h2tcp $gwnode` --lo $OST || exit 2 - let server=$server+1 - let i=$i+1 - done - - let gw=$gw+1 -done - -$LMC_REAL --batch $BATCH -rm -f $BATCH diff --git a/lustre/tests/mcrlov.sh b/lustre/tests/mcrlov.sh deleted file mode 100755 index cce8878..0000000 --- a/lustre/tests/mcrlov.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -config=${1:-mcrlov.xml} - -LMC="../utils/lmc -m $config" - -# TCP/IP servers -SERVERS="ba-ost-1 ba-ost-2" -ROUTER=dev5 -MDS=dev7 -TMP=${TMP:-/tmp} - -# Elan clients -CLIENT_LO=dev2 -CLIENT_HI=dev25 - -TCPBUF=1048576 - - -h2elan () { - echo $1 | sed 's/[^0-9]*//g' -} - -h2tcp () { - echo "${1}" -} - -[ -f $config ] && rm $config - -# Client node -${LMC} --add net --node client --nid '*' --nettype elan || exit 1 -# Router node -${LMC} --add net --router --node $ROUTER --tcpbuf $TCPBUF --nid `h2tcp $ROUTER` --nettype tcp || exit 1 -${LMC} --add net --node $ROUTER --nid `h2elan $ROUTER` --nettype elan|| exit 1 -${LMC} --add route --node $ROUTER --gw `h2elan $ROUTER` --lo `h2elan $CLIENT_LO` --hi `h2elan $CLIENT_HI` --nettype elan || exit 2 - -${LMC} --add net --node $MDS --nid `h2elan $MDS` --nettype elan || exit 1 -${LMC} --add mds --node $MDS --mds mds1 --dev $TMP/mds1 --size 100000 || exit 1 -${LMC} --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 1 - -${LMC} --add mtpt --node client --path /mnt/lustre --mds mds1 --lov lov1 - -for s in $SERVERS - do - # server node - ${LMC} --add net --node $s --tcpbuf $TCPBUF --nid $s --nettype tcp || exit 1 - # route to server - ${LMC} --add route --node $ROUTER --nettype tcp --gw `h2tcp $ROUTER` --lo $s || exit 2 - # the device on the server - #${LMC} --format --lov lov1 --node $s --ost bluearc || exit 3 - ${LMC} --add ost --lov lov1 --node $s --dev bluearc --format || exit 3 -done diff --git a/lustre/tests/mount2fs.sh b/lustre/tests/mount2fs.sh index cd51424..949f447 100644 --- a/lustre/tests/mount2fs.sh +++ b/lustre/tests/mount2fs.sh @@ -15,21 +15,22 @@ MOUNT2=${MOUNT2:-${MOUNT}2} MDSSIZE=50000 FSTYPE=${FSTYPE:-ext3} +STRIPE_BYTES=${STRIPE_BYTES:-1048576} OSTDEV1=${OSTDEV1:-$TMP/ost1-`hostname`} OSTDEV2=${OSTDEV2:-$TMP/ost2-`hostname`} OSTSIZE=100000 MDSNODE=${MDSNODE:-uml1} -OSTNODE=${OSTNODE:-uml1} -CLIENT=${CLIENT:-uml2} -CLIENT2=${CLIENT2:-uml2} +OSTNODE=${OSTNODE:-uml2} +CLIENT=${CLIENT:-client1} +CLIENT2=${CLIENT2:-client2} # create nodes ${LMC} -o $config --add net --node $MDSNODE --nid $MDSNODE --nettype tcp || exit 1 ${LMC} -m $config --add net --node $OSTNODE --nid $OSTNODE --nettype tcp || exit 2 -${LMC} -m $config --add net --node $CLIENT --nid $CLIENT --nettype tcp || exit 3 +${LMC} -m $config --add net --node $CLIENT --nid '*' --nettype tcp || exit 3 if [ "$CLIENT" != "$CLIENT2" ]; then - ${LMC} -m $config --add net --node $CLIENT2 --nid $CLIENT --nettype tcp || exit 3 + ${LMC} -m $config --add net --node $CLIENT2 --nid '*' --nettype tcp || exit 3 fi # configure mds server @@ -37,8 +38,8 @@ ${LMC} -m $config --add mds --node $MDSNODE --mds mds1 --group fs1 --fstype $FST ${LMC} -m $config --add mds --node $MDSNODE --mds mds2 --group fs2 --fstype $FSTYPE --dev $MDSDEV2 --size $MDSSIZE ||exit 10 # configure ost -${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 20 -${LMC} -m $config --add lov --lov lov2 --mds mds2 --stripe_sz 65536 --stripe_cnt 0 --stripe_pattern 0 || exit 20 +${LMC} -m $config --add lov --lov lov1 --mds mds1 --stripe_sz $STRIPE_BYTES --stripe_cnt 0 --stripe_pattern 0 || exit 20 +${LMC} -m $config --add lov --lov lov2 --mds mds2 --stripe_sz $STRIPE_BYTES --stripe_cnt 0 --stripe_pattern 0 || exit 20 ${LMC} -m $config --add ost --node $OSTNODE --group fs1 --lov lov1 --fstype $FSTYPE --dev $OSTDEV1 --size $OSTSIZE || exit 21 ${LMC} -m $config --add ost --node $OSTNODE --group fs2 --lov lov2 --fstype $FSTYPE --dev $OSTDEV2 --size $OSTSIZE || exit 22 diff --git a/lustre/tests/oos.sh b/lustre/tests/oos.sh index 9e08890..907f853 100755 --- a/lustre/tests/oos.sh +++ b/lustre/tests/oos.sh @@ -67,7 +67,7 @@ fi RECORDSOUT=`grep "records out" $LOG | cut -d + -f1` FILESIZE=`ls -l $OOS | awk '{ print $5 }'` -if [ "$RECORDSOUT" -ne $(($FILESIZE / 1024)) ]; then +if [ "$RECORDSOUT" -ne $((FILESIZE / 1024)) ]; then echo "ERROR: blocks written by dd not equal to the size of file" SUCCESS=0 fi diff --git a/lustre/tests/quota_sanity.sh b/lustre/tests/quota_sanity.sh index 9261668..59f1b72 100644 --- a/lustre/tests/quota_sanity.sh +++ b/lustre/tests/quota_sanity.sh @@ -13,10 +13,12 @@ USER="quota_usr" TSTID=${TSTID:-60000} RUNAS=${RUNAS:-"runas -u $TSTID"} BLK_SZ=1024 -BUNIT_SZ=10 # 10 quota blocks -BTUNE_SZ=5 # 5 quota blocks +BUNIT_SZ=1000 # 1000 quota blocks +BTUNE_SZ=500 # 500 quota blocks IUNIT_SZ=10 # 10 files ITUNE_SZ=5 # 5 files +MAX_DQ_TIME=604800 +MAX_IQ_TIME=604800 MOUNT="`cat /proc/mounts | grep "lustre" | awk '{print $2}'`" if [ -z "$MOUNT" ]; then @@ -26,37 +28,46 @@ fi OSTCOUNT=`cat /proc/fs/lustre/lov/*/activeobd | head -n 1` TSTDIR="$MOUNT/quota_dir" -# set_blk_tunables(bunit_sz, btune_sz) -set_blk_tunables() { - # set bunit and btune size on all obdfilters +# set_blk_tunables(btune_sz) +set_blk_tunesz() { + # set btune size on all obdfilters for i in `ls /proc/fs/lustre/obdfilter/*/quota_btune_sz`; do - echo $(($2 * $BLK_SZ)) > $i + echo $(($1 * $BLK_SZ)) > $i + done + # set btune size on mds + for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do + echo $(($1 * $BLK_SZ)) > $i done +} + +# se_blk_unitsz(bunit_sz) +set_blk_unitsz() { for i in `ls /proc/fs/lustre/obdfilter/*/quota_bunit_sz`; do echo $(($1 * $BLK_SZ)) > $i - done; - # set bunit and btune size on mds - for i in `ls /proc/fs/lustre/mds/mds*/quota_btune_sz`; do - echo $(($2 * $BLK_SZ)) > $i done for i in `ls /proc/fs/lustre/mds/mds*/quota_bunit_sz`; do echo $(($1 * $BLK_SZ)) > $i done } -# set_file_tunables(iunit_sz, itune_sz) -set_file_tunables() { +# set_file_tunesz(itune_sz) +set_file_tunesz() { # set iunit and itune size on all obdfilters for i in `ls /proc/fs/lustre/obdfilter/*/quota_itune_sz`; do - echo $2 > $i - done - for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do echo $1 > $i - done; + done # set iunit and itune size on mds for i in `ls /proc/fs/lustre/mds/mds*/quota_itune_sz`; do - echo $2 > $i + echo $1 > $i done + + +} +# set_file_unitsz(iunit_sz) +set_file_unitsz() { + for i in `ls /proc/fs/lustre/obdfilter/*/quota_iunit_sz`; do + echo $1 > $i + done; for i in `ls /proc/fs/lustre/mds/mds*/quota_iunit_sz`; do echo $1 > $i done @@ -77,27 +88,29 @@ prepare_test() { fi RUNAS="runas -u $TSTID" + # set block tunables - - set_blk_tunables $BUNIT_SZ $BTUNE_SZ + set_blk_tunesz $BTUNE_SZ + set_blk_unitsz $BUNIT_SZ # set file tunaables - set_file_tunables $IUNIT_SZ $ITUNE_SZ + set_file_tunesz $ITUNE_SZ + set_file_unitsz $IUNIT_SZ [ -d $TSTDIR ] || mkdir $TSTDIR chmod 777 $TSTDIR } -cleanup_test() { - # delete test user and group - userdel "$USER" - groupdel "$USER" - +cleanup_test() { # restore block tunables to default size - set_blk_tunables $((1024 * 100)) $((1024 * 50)) + set_blk_unitsz $((1024 * 100)) + set_blk_tunesz $((1024 * 50)) # restore file tunables to default size - set_file_tunables 5000 2500 + set_file_unitsz 5000 + set_file_tunesz 2500 rm -fr $TSTDIR + # delete test user and group + userdel "$USER" } # set quota @@ -105,6 +118,9 @@ test_1() { echo "== Enable quota" $LFS quotaoff -ug $MOUNT $LFS quotacheck -ug $MOUNT + + $LFS setquota -u $USER 0 0 0 0 $MOUNT + $LFS setquota -g $USER 0 0 0 0 $MOUNT return 0 } @@ -123,7 +139,8 @@ test_2() { $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(usr) write failure, but expect success" echo " Done" echo " Write out of block quota ..." - $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 + # this time maybe cache write, ignore it's failure + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null # flush cache, ensure noquota flag is setted on client sync; sleep 1; sync; $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(usr) write success, but expect EDQUOT" @@ -142,7 +159,8 @@ test_2() { $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) > /dev/null 2>&1 || error "(grp) write failure, but expect success" echo " Done" echo " Write out of block quota ..." - $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 + # this time maybe cache write, ignore it's failure + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$(($LIMIT/2)) seek=$(($LIMIT/2)) > /dev/null 2>&1 || echo " " > /dev/null sync; sleep 1; sync; $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$LIMIT > /dev/null 2>&1 && error "(grp) write success, but expect EDQUOT" echo " EDQUOT" @@ -197,17 +215,135 @@ test_3() { return 0 } +test_block_soft() { + TESTFILE=$1 + GRACE=$2 + BS=$(($BUNIT_SZ * $BLK_SZ)) + + echo " Write to exceed soft limit" + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success" + sync; sleep 1; sync; + + echo " Write before timer goes off" + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success" + sync; sleep 1; sync; + echo " Done" + + echo " Sleep $GRACE seconds ..." + sleep $GRACE + + echo " Write after timer goes off" + # maybe cache write, ignore. + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ seek=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null + sync; sleep 1; sync; + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=1 seek=$(($BUNIT_SZ * 3)) >/dev/null 2>&1 && error "write success, but expect EDQUOT" + echo " EDQUOT" + + echo " Unlink file to stop timer" + rm -f $TESTFILE + echo " Done" + + echo " Write ..." + $RUNAS dd if=/dev/zero of=$TESTFILE bs=$BLK_SZ count=$BUNIT_SZ >/dev/null 2>&1 || error "write failure, but expect success" + echo " Done" + + # cleanup + rm -f $TESTFILE +} + # block soft limit (start timer, timer goes off, stop timer) test_4() { echo "== Block soft limit" - echo " ** skipped" + LIMIT=$(( $BUNIT_SZ * $(($OSTCOUNT + 1)) )) # 1 bunits each sever + TESTFILE="$TSTDIR/quota_tst40" + GRACE=10 + + echo " User quota (soft limit: $LIMIT bytes grace: $GRACE seconds)" + $LFS setquota -t -u $GRACE $MAX_IQ_TIME $MOUNT + $LFS setquota -u $USER $LIMIT 0 0 0 $MOUNT + + test_block_soft $TESTFILE $GRACE + $LFS setquota -u $USER 0 0 0 0 $MOUNT + + echo " Group quota (soft limit: $LIMIT bytes grace: $GRACE seconds)" + $LFS setquota -t -g $GRACE $MAX_IQ_TIME $MOUNT + $LFS setquota -g $USER $LIMIT 0 0 0 $MOUNT + TESTFILE="$TSTDIR/quota_tst41" + + test_block_soft $TESTFILE $GRACE + $LFS setquota -g $USER 0 0 0 0 $MOUNT + return 0 } +test_file_soft() { + TESTFILE=$1 + LIMIT=$2 + GRACE=$3 + + echo " Create files to exceed soft limit" + for i in `seq $LIMIT`; do + $RUNAS touch ${TESTFILE}_$i >/dev/null 2>&1 || error "touch failure, but expect success" + done + echo " Done" + + echo " Create file before timer goes off" + $RUNAS touch ${TESTFILE}_before >/dev/null 2>&1 || error "touch before timer goes off failure, but expect success" + echo " Done" + + echo " Sleep $GRACE seconds ..." + sleep $GRACE + + echo " Create file after timer goes off" + for i in `seq $(($IUNIT_SZ - 1))`; do + $RUNAS touch ${TESTFILE}_after_$i >/dev/null 2>&1 || error "touch ${TESTFILE}_after_$i failure, but expect success" + done + $RUNAS touch ${TESTFILE}_after >/dev/null 2>&1 && error "touch after timer goes off success, but expect EDQUOT" + echo " EDQUOT" + + echo " Unlink files to stop timer" + for i in `seq $LIMIT`; do + rm -f ${TESTFILE}_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_$i failure" + done + rm -f ${TESTFILE}_before + for i in `seq $(($IUNIT_SZ - 1))`; do + rm -f ${TESTFILE}_after_$i >/dev/null 2>&1 || error "rm ${TESTFILE}_after_$i failure" + done + echo " Done" + + echo " Create file" + $RUNAS touch ${TESTFILE}_xxx >/dev/null 2>&1 || error "touch after timer stop failure, but expect success" + echo " Done" + + # cleanup + rm -f ${TESTFILE}_xxx +} + # file soft limit (start timer, timer goes off, stop timer) test_5() { echo "== File soft limit" - echo " ** skipped" + LIMIT=$(($IUNIT_SZ * 10)) # 10 iunits on mds + TESTFILE="$TSTDIR/quota_tst50" + GRACE=5 + + echo " User quota (soft limit: $LIMIT files grace: $GRACE seconds)" + $LFS setquota -t -u $MAX_DQ_TIME $GRACE $MOUNT + $LFS setquota -u $USER 0 0 $LIMIT 0 $MOUNT + + test_file_soft $TESTFILE $LIMIT $GRACE + $LFS setquota -u $USER 0 0 0 0 $MOUNT + + echo " Group quota (soft limit: $LIMIT files grace: $GRACE seconds)" + $LFS setquota -t -g $MAX_DQ_TIME $GRACE $MOUNT + $LFS setquota -g $USER 0 0 $LIMIT 0 $MOUNT + TESTFILE="$TSTDIR/quota_tst51" + + test_file_soft $TESTFILE $LIMIT $GRACE + $LFS setquota -g $USER 0 0 0 0 $MOUNT + + # cleanup + $LFS setquota -t -u $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT + $LFS setquota -t -g $MAX_DQ_TIME $MAX_IQ_TIME $MOUNT return 0 } @@ -270,8 +406,8 @@ test_7() { $RUNAS dd if=/dev/zero of=$FILEB bs=$BLK_SZ seek=$LIMIT count=$BUNIT_SZ >/dev/null 2>&1 && error "write fileb success, but expect EDQUOT" sync; sleep 1; sync; echo " Write to OST0 return EDQUOT" - # this write of OST0 is cache write, will success - $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || error "write filea failure, but expect success" + # this write maybe cache write, ignore it's failure + $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) >/dev/null 2>&1 || echo " " > /dev/null sync; sleep 1; sync; $RUNAS dd if=/dev/zero of=$FILEA bs=$BLK_SZ count=$(($BUNIT_SZ * 2)) seek=$(($BUNIT_SZ *2)) >/dev/null 2>&1 && error "write filea success, but expect EDQUOT" echo " EDQUOT" diff --git a/lustre/tests/recovery-small.sh b/lustre/tests/recovery-small.sh index 2980fdf..e0d4b3c 100755 --- a/lustre/tests/recovery-small.sh +++ b/lustre/tests/recovery-small.sh @@ -2,8 +2,8 @@ set -e -# bug 2986 -ALWAYS_EXCEPT="20b" +# bug 2986 5494 +ALWAYS_EXCEPT="20b 24" LUSTRE=${LUSTRE:-`dirname $0`/..} @@ -136,14 +136,14 @@ run_test 9 "pause bulk on OST (bug 1420)" #bug 1521 test_10() { - do_facet client mcreate $MOUNT/f10 || return 1 - drop_bl_callback "chmod 0777 $MOUNT/f10" || return 2 + do_facet client mcreate $MOUNT/$tfile || return 1 + drop_bl_callback "chmod 0777 $MOUNT/$tfile" || return 2 # wait for the mds to evict the client #echo "sleep $(($TIMEOUT*2))" #sleep $(($TIMEOUT*2)) - do_facet client touch $MOUNT/f10 || echo "touch failed, evicted" - do_facet client checkstat -v -p 0777 $MOUNT/f10 || return 3 - do_facet client "munlink $MOUNT/f10" + do_facet client touch $MOUNT/$tfile || echo "touch failed, evicted" + do_facet client checkstat -v -p 0777 $MOUNT/$tfile || return 3 + do_facet client "munlink $MOUNT/$tfile" } run_test 10 "finish request on server after client eviction (bug 1521)" @@ -393,12 +393,75 @@ test_24() { # bug 2248 - eviction fails writeback but app doesn't see it client_reconnect [ $rc -eq 0 ] && error "multiop didn't fail fsync: rc $rc" || true } -run_test 24 "fsync error (should return error)" +run_test 24 "fsync error (should return error)" + +test_26() { # bug 5921 - evict dead exports +# this test can only run from a client on a separate node. + [ "`lsmod | grep obdfilter`" ] && \ + echo "skipping test 26 (local OST)" && return + [ "`lsmod | grep mds`" ] && \ + echo "skipping test 26 (local MDS)" && return + OST_FILE=/proc/fs/lustre/obdfilter/ost_svc/num_exports + OST_EXP="`do_facet ost cat $OST_FILE`" + OST_NEXP1=`echo $OST_EXP | cut -d' ' -f2` + echo starting with $OST_NEXP1 OST exports +# OBD_FAIL_PTLRPC_DROP_RPC 0x505 + do_facet client sysctl -w lustre.fail_loc=0x505 + # evictor takes up to 2.25x to evict. But if there's a + # race to start the evictor from various obds, the loser + # might have to wait for the next ping. + echo Waiting for $(($TIMEOUT * 4)) secs + sleep $(($TIMEOUT * 4)) + OST_EXP="`do_facet ost cat $OST_FILE`" + OST_NEXP2=`echo $OST_EXP | cut -d' ' -f2` + echo ending with $OST_NEXP2 OST exports + do_facet client sysctl -w lustre.fail_loc=0x0 + [ $OST_NEXP1 -le $OST_NEXP2 ] && error "client not evicted" + return 0 +} +run_test 26 "evict dead exports" + +test_27() { + [ "`lsmod | grep mds`" ] || \ + { echo "skipping test 27 (non-local MDS)" && return 0; } + mkdir -p $DIR/$tdir + writemany -q -a $DIR/$tdir/$tfile 0 5 & + CLIENT_PID=$! + sleep 1 + FAILURE_MODE="SOFT" + facet_failover mds +#define OBD_FAIL_OSC_SHUTDOWN 0x407 + sysctl -w lustre.fail_loc=0x80000407 + # need to wait for reconnect + echo -n waiting for fail_loc + while [ `sysctl -n lustre.fail_loc` -eq -2147482617 ]; do + sleep 1 + echo -n . + done + facet_failover mds + #no crashes allowed! + kill -USR1 $CLIENT_PID + wait $CLIENT_PID + true +} +run_test 27 "fail LOV while using OSC's" -test_25a() { +test_28() { # bug 6086 - error adding new clients + do_facet client mcreate $MOUNT/$tfile || return 1 + drop_bl_callback "chmod 0777 $MOUNT/$tfile" || return 2 + #define OBD_FAIL_MDS_ADD_CLIENT 0x12f + do_facet mds sysctl -w lustre.fail_loc=0x8000012f + # fail once (evicted), reconnect fail (fail_loc), ok + df || (sleep 1; df) || (sleep 1; df) || error "reconnect failed" + rm -f $MOUNT/$tfile + fail mds # verify MDS last_rcvd can be loaded +} +run_test 28 "handle error adding new clients (bug 6086)" + +test_50() { mkdir -p $DIR/$tdir - # put a load of file creates/writes/deletes for 10 min. - do_facet client "writemany -q -a $DIR/$tdir/$tfile 600 5" & + # put a load of file creates/writes/deletes + writemany -q $DIR/$tdir/$tfile 0 5 & CLIENT_PID=$! echo writemany pid $CLIENT_PID sleep 10 @@ -410,42 +473,46 @@ test_25a() { sleep 60 fail mds # client process should see no problems even though MDS went down + sleep $TIMEOUT + kill -USR1 $CLIENT_PID wait $CLIENT_PID rc=$? echo writemany returned $rc + #these may fail because of eviction due to slow AST response. return $rc } -run_test 25a "failover MDS under load" +run_test 50 "failover MDS under load" -test_25b() { +test_51() { mkdir -p $DIR/$tdir # put a load of file creates/writes/deletes - do_facet client "writemany -q -a $DIR/$tdir/$tfile 300 5" & + writemany -q $DIR/$tdir/$tfile 0 5 & CLIENT_PID=$! - echo writemany pid $CLIENT_PID sleep 1 FAILURE_MODE="SOFT" facet_failover mds # failover at various points during recovery - sleep 1 - facet_failover mds - sleep 5 - facet_failover mds - sleep 10 - facet_failover mds - sleep 20 - facet_failover mds + SEQ="1 5 10 $(seq $TIMEOUT 5 $(($TIMEOUT+10)))" + echo will failover at $SEQ + for i in $SEQ + do + echo failover in $i sec + sleep $i + facet_failover mds + done # client process should see no problems even though MDS went down # and recovery was interrupted + sleep $TIMEOUT + kill -USR1 $CLIENT_PID wait $CLIENT_PID rc=$? echo writemany returned $rc return $rc } -run_test 25b "failover MDS during recovery" +run_test 51 "failover MDS during recovery" -test_25c_guts() { - do_facet client "writemany -q $DIR/$tdir/$tfile 600 5" & +test_52_guts() { + do_facet client "writemany -q -a $DIR/$tdir/$tfile 300 5" & CLIENT_PID=$! echo writemany pid $CLIENT_PID sleep 10 @@ -461,22 +528,23 @@ test_25c_guts() { return $rc } -test_25c() { +test_52() { mkdir -p $DIR/$tdir - test_25c_guts + test_52_guts rc=$? [ $rc -ne 0 ] && { return $rc; } # wait for client to reconnect to OST sleep 30 - test_25c_guts + test_52_guts rc=$? [ $rc -ne 0 ] && { return $rc; } sleep 30 - test_25c_guts + test_52_guts rc=$? client_reconnect - return $rc + #return $rc } -run_test 25c "failover OST under load" +run_test 52 "failover OST under load" + FORCE=--force $CLEANUP diff --git a/lustre/tests/replay-single.sh b/lustre/tests/replay-single.sh index 15a2c99..1e7268f 100755 --- a/lustre/tests/replay-single.sh +++ b/lustre/tests/replay-single.sh @@ -672,7 +672,7 @@ test_33() { touch $DIR/$tfile fail_abort mds # this file should be gone, because the replay was aborted - $CHECKSTAT -t file $DIR/$tfile && return 1 + $CHECKSTAT -t file $DIR/$tfile && return 3 return 0 } run_test 33 "abort recovery before client does replay" @@ -837,6 +837,8 @@ test_42() { createmany -o $DIR/$tfile-%d 800 replay_barrier ost unlinkmany $DIR/$tfile-%d 0 400 + DEBUG42=`sysctl portals.debug | tr -d ' '` + sysctl -w portals.debug=-1 facet_failover ost # osc is evicted, fs is smaller @@ -844,6 +846,7 @@ test_42() { [ $blocks_after -lt $blocks ] || return 1 echo wait for MDS to timeout and recover sleep $((TIMEOUT * 2)) + sysctl -w $DEBUG42 unlinkmany $DIR/$tfile-%d 400 400 $CHECKSTAT -t file $DIR/$tfile-* && return 2 || true } @@ -1020,13 +1023,15 @@ run_test 57 "test recovery from llog for setattr op" test_58() { #define OBD_FAIL_MDS_OST_SETATTR 0x12c do_facet mds "sysctl -w lustre.fail_loc=0x8000012c" - createmany -o $DIR/$tfile-%d 30000 + mkdir $DIR/$tdir + createmany -o $DIR/$tdir/$tfile-%d 2500 replay_barrier mds fail mds sleep 2 - $CHECKSTAT -t file $DIR/$tfile-* || return 1 + $CHECKSTAT -t file $DIR/$tdir/$tfile-* || return 1 do_facet mds "sysctl -w lustre.fail_loc=0x0" - rm -f $DIR/$tfile-* + unlinkmany $DIR/$tdir/$tfile-%d 2500 + rmdir $DIR/$tdir } run_test 58 "test recovery from llog for setattr op (test llog_gen_rec)" diff --git a/lustre/tests/routed.sh b/lustre/tests/routed.sh new file mode 100644 index 0000000..c6d6bdb --- /dev/null +++ b/lustre/tests/routed.sh @@ -0,0 +1,157 @@ +#!/bin/sh -vx + +set -e + +export PATH=`dirname $0`/../utils:$PATH + +config=${1:-`basename $0 .sh`.xml} + +BATCH=/tmp/lmc-batch.$$ +save_cmd() { + echo "$@" >> $BATCH +} + +LMC_REAL="${LMC:-lmc} -m $config" +LMC="save_cmd" + +TMP=${TMP:-/tmp} + +MOUNT=${MOUNT:-/mnt/lustre} + +STRIPE_BYTES=$((1024*1024)) +STRIPES_PER_OBJ=1 + +# specific journal size for the ost, in MB +JSIZE=${JSIZE:-0} +JARG="" +[ "$JSIZE" -gt 0 ] && JARG="--journal_size $JSIZE" + +MDSNODE=${MDSNODE:-srv1} +MDSDEV=${MDSDEV:-$TMP/mds1-$MDSNODE} +MDSSIZE=${MDSSIZE:-400000} +OSTNODES=${OSTNODES:-"srv2 srv3 srv2 srv3"} +OSTSIZE=${OSTSIZE:-150000} +# OSTDEVN will still override the device for OST N +OSTFAILOVER=${OSTFAILOVER:---failover} +ROUTERS=${ROUTERS:-"cli1 cli2"} +CLIENT_NETTYPE=elan +CLIENT_CLUSTER=${CLIENT_CLUSTER:-0x0000} +CLIENT_LO=${CLIENT_LO:-cli3} +CLIENT_HI=${CLIENT_HI:-cli4} +CLIENTS=${CLIENTS:-client} +SERVER_NETTYPE=tcp +SERVER_CLUSTER=${SERVER_CLUSTER:-0x1000} +FSNAME=fs1 + +h2tcp () { + case $1 in + client) echo '\*' ;; + *) echo $1 ;; + esac +} + +h2elan () { # assumes node names of the form fooN, where N is elan node ID + case $1 in + client) echo '\*' ;; + *) echo $1 | sed "s/[^0-9]*//" ;; + esac +} + +rm -f $config $BATCH + +# MDSNODE +echo; echo -n "adding MDS on: $MDSNODE" +eval "NODE$MDSNODE=y" +$LMC --add net --node $MDSNODE --nid `h2$SERVER_NETTYPE $MDSNODE` \ + --nettype $SERVER_NETTYPE --cluster_id $SERVER_CLUSTER +$LMC --add mds --node $MDSNODE --mds mds-$FSNAME --dev $MDSDEV $MDSOPT \ + --size $MDSSIZE +$LMC --add lov --lov lov-$FSNAME --mds mds-$FSNAME --stripe_sz $STRIPE_BYTES \ + --stripe_cnt $STRIPES_PER_OBJ --stripe_pattern 0 +# MDS route to elan client +for R in $ROUTERS; do + echo -n " [r=$R]" + $LMC --node $MDSNODE --add route --nettype $SERVER_NETTYPE \ + --gw `h2$CLIENT_NETTYPE $R` \ + --lo `h2$CLIENT_NETTYPE $CLIENT_LO` \ + --hi `h2$CLIENT_NETTYPE $CLIENT_HI` \ + --gateway_cluster_id $SERVER_CLUSTER \ + --target_cluster_id $SERVER_CLUSTER +done + +--dev $OSTDEV --size $OSTSIZE +# OSTNODE +COUNT=1 +for OSTNODE in $OSTNODES; do + OST=ost$COUNT + eval OSTDEV=\$OSTDEV$COUNT + if [ -z "$OSTDEV" ]; then + eval OSTDEV=${!OSTDEV:=$TMP/$OST-$OSTNODE} + fi + DEV=`basename $OSTDEV` + echo; echo -n "adding OST on: $OSTNODE[$DEV]" + if [ "`eval echo \\$NODE$OSTNODE`" != "y" ]; then + $LMC --add net --node $OSTNODE --nid $OSTNODE \ + --nettype $SERVER_NETTYPE --cluster_id $SERVER_CLUSTER + # OST route to elan clients + for R in $ROUTERS; do + echo -n " [r=$R]" + $LMC --node $OSTNODE --add route \ + --nettype $SERVER_NETTYPE \ + --gw `h2$CLIENT_NETTYPE $R` \ + --lo `h2$CLIENT_NETTYPE $CLIENT_LO` \ + --hi `h2$CLIENT_NETTYPE $CLIENT_HI` \ + --gateway_cluster_id $SERVER_CLUSTER \ + --target_cluster_id $SERVER_CLUSTER + done + eval "NODE$OSTNODE=y" + fi + + $LMC --add ost --node $OSTNODE --ost ost-$FSNAME-$OSTNODE-$DEV \ + --lov lov-$FSNAME $OSTFAILOVER --dev $OSTDEV --size $OSTSIZE \ + $OSTOPT + COUNT=`expr $COUNT + 1` +done + +# ROUTER +echo; echo -n "adding ROUTER on: " +for ROUTER in $ROUTERS; do + echo -n " $ROUTER" + $LMC --node $ROUTER --add net --nid `h2$CLIENT_NETTYPE $ROUTER` \ + --cluster_id $SERVER_CLUSTER --nettype $SERVER_NETTYPE \ + --hostaddr $ROUTER --router + $LMC --node $ROUTER --add net --nid `h2$CLIENT_NETTYPE $ROUTER` \ + --cluster_id $CLIENT_CLUSTER --nettype $CLIENT_NETTYPE \ + --router + # ROUTER route to OSTs and MDS + for NODE in `echo $MDSNODE $OSTNODES | tr -s " " "\n" | sort -u`; do + $LMC --node $ROUTER --add route \ + --nettype $SERVER_NETTYPE \ + --gw `h2$CLIENT_NETTYPE $ROUTER` \ + --lo `h2$SERVER_NETTYPE $NODE` \ + --gateway_cluster_id $SERVER_CLUSTER \ + --target_cluster_id $SERVER_CLUSTER + done + # ROUTER route to clients + $LMC --node $ROUTER --add route --nettype $CLIENT_NETTYPE \ + --gw `h2$CLIENT_NETTYPE $ROUTER` \ + --lo `h2$CLIENT_NETTYPE $CLIENT_LO` \ + --hi `h2$CLIENT_NETTYPE $CLIENT_HI` \ + --gateway_cluster_id $CLIENT_CLUSTER \ + --target_cluster_id $CLIENT_CLUSTER +done + +# CLIENT +echo; echo -n "adding CLIENT on: " +for CLIENT in $CLIENTS; do + echo -n " $CLIENT" + $LMC --node $CLIENT --add net --nid `h2$CLIENT_NETTYPE $CLIENT` \ + --cluster_id $CLIENT_CLUSTER --nettype $CLIENT_NETTYPE + $LMC --node $CLIENT --add mtpt --path $MOUNT --mds mds-$FSNAME \ + --lov lov-$FSNAME $CLIENTOPT +done +echo + +set -vx +echo "generating config $config from $BATCH" +$LMC_REAL --batch $BATCH diff --git a/lustre/tests/runtests b/lustre/tests/runtests index f31295a..75f8765 100755 --- a/lustre/tests/runtests +++ b/lustre/tests/runtests @@ -130,5 +130,6 @@ if [ `expr $NOWUSED - $USED` -gt 1024 ]; then fi if [ "$I_MOUNTED" = "yes" ]; then + sync && sleep 2 && sync # wait for delete thread sh llmountcleanup.sh || exit 29 fi diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index 915860e..11874aa 100644 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -55,14 +55,12 @@ SAVE_PWD=$PWD clean() { echo -n "cln.." sh llmountcleanup.sh ${FORCE} > /dev/null || exit 20 - I_MOUNTED=no } CLEAN=${CLEAN:-:} start() { echo -n "mnt.." sh llrmount.sh > /dev/null || exit 10 - I_MOUNTED=yes echo "done" } START=${START:-:} @@ -811,9 +809,7 @@ run_test 27a "one stripe file ==================================" test_27c() { [ "$OSTCOUNT" -lt "2" ] && echo "skipping 2-stripe test" && return - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi + mkdir -p $DIR/d27 $LSTRIPE $DIR/d27/f01 65536 0 2 || error "lstripe failed" [ `$LFIND $DIR/d27/f01 | grep -A 10 obdidx | wc -l` -eq 4 ] || error "two-stripe file doesn't have two stripes" @@ -824,9 +820,7 @@ test_27c() { run_test 27c "create two stripe file f01 =======================" test_27d() { - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi + mkdir -p $DIR/d27 $LSTRIPE $DIR/d27/fdef 0 -1 0 || error "lstripe failed" $CHECKSTAT -t file $DIR/d27/fdef || error "checkstat failed" #dd if=/dev/zero of=$DIR/d27/fdef bs=4k count=4 || error @@ -834,9 +828,7 @@ test_27d() { run_test 27d "create file with default settings ================" test_27e() { - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi + mkdir -p $DIR/d27 $LSTRIPE $DIR/d27/f12 65536 0 2 || error "lstripe failed" $LSTRIPE $DIR/d27/f12 65536 0 2 && error "lstripe succeeded twice" $CHECKSTAT -t file $DIR/d27/f12 || error "checkstat failed" @@ -844,9 +836,7 @@ test_27e() { run_test 27e "lstripe existing file (should return error) ======" test_27f() { - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi + mkdir -p $DIR/d27 $LSTRIPE $DIR/d27/fbad 100 0 1 && error "lstripe failed" dd if=/dev/zero of=$DIR/d27/f12 bs=4k count=4 || error "dd failed" $LFIND $DIR/d27/fbad || error "lfind failed" @@ -854,9 +844,7 @@ test_27f() { run_test 27f "lstripe with bad stripe size (should return error)" test_27g() { - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi + mkdir -p $DIR/d27 $MCREATE $DIR/d27/fnone || error "mcreate failed" pass log "== test 27h: lfind with no objects ============================" @@ -869,14 +857,13 @@ test_27g() { run_test 27g "test lfind =======================================" test_27j() { - if [ ! -d $DIR/d27 ]; then - mkdir $DIR/d27 - fi - $LSTRIPE $DIR/d27/f27j 65536 $OSTCOUNT 1 && error "lstripe failed"||true + mkdir -p $DIR/d27 + $LSTRIPE $DIR/d27/f27j 65536 $OSTCOUNT 1 && error "lstripe failed"||true } run_test 27j "lstripe with bad stripe offset (should return error)" test_27k() { # bug 2844 + mkdir -p $DIR/d27 FILE=$DIR/d27/f27k LL_MAX_BLKSIZE=$((4 * 1024 * 1024)) [ ! -d $DIR/d27 ] && mkdir -p $DIR/d27 @@ -890,6 +877,7 @@ test_27k() { # bug 2844 run_test 27k "limit i_blksize for broken user apps =============" test_27l() { + mkdir -p $DIR/d27 mcreate $DIR/f27l || error "creating file" $RUNAS $LSTRIPE $DIR/f27l 65536 -1 1 && \ error "lstripe should have failed" || true @@ -897,32 +885,129 @@ test_27l() { run_test 27l "check setstripe permissions (should return error)" test_27m() { - [ "$OSTCOUNT" -lt "2" ] && echo "skipping out-of-space test on OST0" && return - if [ $ORIGFREE -gt $MAXFREE ]; then - echo "skipping out-of-space test on OST0" - return - fi - mkdir -p $DIR/d27 - $LSTRIPE $DIR/d27/f27m_1 0 0 1 - dd if=/dev/zero of=$DIR/d27/f27m_1 bs=1024 count=$MAXFREE && \ - error "dd should fill OST0" - i=2 - while $LSTRIPE $DIR/d27/f27m_$i 0 0 1 ; do - i=`expr $i + 1` - [ $i -gt 256 ] && break - done - i=`expr $i + 1` - touch $DIR/d27/f27m_$i - [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \ - error "OST0 was full but new created file still use it" - i=`expr $i + 1` - touch $DIR/d27/f27m_$i - [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \ - error "OST0 was full but new created file still use it" - rm $DIR/d27/f27m_1 + [ "$OSTCOUNT" -lt "2" ] && echo "skipping out-of-space test on OST0" && return + if [ $ORIGFREE -gt $MAXFREE ]; then + echo "skipping out-of-space test on OST0" + return + fi + mkdir -p $DIR/d27 + $LSTRIPE $DIR/d27/f27m_1 0 0 1 + dd if=/dev/zero of=$DIR/d27/f27m_1 bs=1024 count=$MAXFREE && \ + error "dd should fill OST0" + i=2 + while $LSTRIPE $DIR/d27/f27m_$i 0 0 1 ; do + i=`expr $i + 1` + [ $i -gt 256 ] && break + done + i=`expr $i + 1` + touch $DIR/d27/f27m_$i + [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \ + error "OST0 was full but new created file still use it" + i=`expr $i + 1` + touch $DIR/d27/f27m_$i + [ `$LFIND $DIR/d27/f27m_$i | grep -A 10 obdidx | awk '{print $1}'| grep -w "0"` ] && \ + error "OST0 was full but new created file still use it" + rm $DIR/d27/f27m_1 } run_test 27m "create file while OST0 was full ==================" +# osc's keep a NOSPC stick flag that gets unset with rmdir +reset_enospc() { + sysctl -w lustre.fail_loc=0 + mkdir -p $DIR/d27/nospc + rmdir $DIR/d27/nospc +} + +exhaust_precreations() { + local i + ostidx=$1 + ost=$(head -n $(( ostidx + 1 )) /proc/fs/lustre/lov/${LOVNAME}/target_obd | tail -n 1 | awk '{print $2}' | sed -e 's/_UUID$//') + mds=$(find /proc/fs/lustre/mds/ -maxdepth 1 -type d | tail -n 1) + mds=$(basename $mds) + + last_id=$(tail -n 1 /proc/fs/lustre/osc/OSC_*_${ost}_${mds}/prealloc_last_id) + next_id=$(tail -n 1 /proc/fs/lustre/osc/OSC_*_${ost}_${mds}/prealloc_next_id) + + mkdir -p $DIR/d27/${ost} + $LSTRIPE $DIR/d27/${ost} 0 $ostidx 1 + sysctl -w lustre.fail_loc=0x215 + echo "Creating to objid $last_id on ost $ost..." + for (( i = next_id; i <= last_id; i++ )) ; do + touch $DIR/d27/${ost}/f$i + done + reset_enospc +} + +exhaust_all_precreations() { + local i + for (( i=0; i < OSTCOUNT; i++ )) ; do + exhaust_precreations $i + done +} + +test_27n() { + [ "$OSTCOUNT" -lt "2" ] && echo "" && return + reset_enospc + rm -f $DIR/d27/f27n + exhaust_precreations 0 + sysctl -w lustre.fail_loc=0x80000215 + touch $DIR/d27/f27n || error + reset_enospc +} +run_test 27n "creating a file while some OSTs are full (should succeed) ===" + +test_27o() { + [ "$OSTCOUNT" -lt "2" ] && echo "" && return + reset_enospc + rm -f $DIR/d27/f27o + exhaust_all_precreations + sysctl -w lustre.fail_loc=0x215 + touch $DIR/d27/f27o && error + reset_enospc +} +run_test 27o "creating a file while all OSTs are full (should error) ===" + +test_27p() { + [ "$OSTCOUNT" -lt "2" ] && echo "" && return + reset_enospc + rm -f $DIR/d27/f27p + exhaust_precreations 0 + $MCREATE $DIR/d27/f27p || error + $TRUNCATE $DIR/d27/f27p 80000000 || error + $CHECKSTAT -s 80000000 $DIR/d27/f27p || error + sysctl -w lustre.fail_loc=0x80000215 + echo foo >> $DIR/d27/f27p || error + $CHECKSTAT -s 80000004 $DIR/d27/f27p || error + reset_enospc +} +run_test 27p "appending to a truncated file while some OSTs are full ===" + +test_27q() { + [ "$OSTCOUNT" -lt "2" ] && echo "" && return + reset_enospc + rm -f $DIR/d27/f27q + exhaust_precreations 0 + $MCREATE $DIR/d27/f27q || error + $TRUNCATE $DIR/d27/f27q 80000000 || error + $CHECKSTAT -s 80000000 $DIR/d27/f27q || error + sysctl -w lustre.fail_loc=0x215 + echo foo >> $DIR/d27/f27q && error + $CHECKSTAT -s 80000000 $DIR/d27/f27q || error + reset_enospc +} +run_test 27q "appending to a truncated file while all OSTs are full (should error) ===" + +test_27r() { + [ "$OSTCOUNT" -lt "2" ] && echo "" && return + reset_enospc + rm -f $DIR/d27/f27r + exhaust_precreations 0 + sysctl -w lustre.fail_loc=0x80000215 + $LSTRIPE $DIR/d27/f27r 0 0 -1 && error + reset_enospc +} +run_test 27r "creating a file while some OSTs are full with an explicit stripe count (should error) ===" + test_28() { mkdir $DIR/d28 $CREATETEST $DIR/d28/ct || error @@ -1324,6 +1409,20 @@ test_34e() { } run_test 34e "create objects, some with size and some without ==" +test_34f() { # bug 6242, 6243 + SIZE34F=48000 + rm -f $DIR/f34f + $MCREATE $DIR/f34f || error + $TRUNCATE $DIR/f34f $SIZE34F || error "truncating $DIR/f3f to $SIZE34F" + dd if=$DIR/f34f of=$TMP/f34f + $CHECKSTAT -s $SIZE34F $TMP/f34f || error "$TMP/f34f not $SIZE34F bytes" + dd if=/dev/zero of=$TMP/f34fzero bs=$SIZE34F count=1 + cmp $DIR/f34f $TMP/f34fzero || error "$DIR/f34f not all zero" + cmp $TMP/f34f $TMP/f34fzero || error "$TMP/f34f not all zero" + rm $TMP/f34f $TMP/f34fzero $DIR/f34f +} +run_test 34f "read from a file with no objects until EOF =======" + test_35a() { cp /bin/sh $DIR/f35a chmod 444 $DIR/f35a @@ -1956,6 +2055,15 @@ test_54d() { } run_test 54d "fifo device works in lustre ======================" +test_54e() { + check_kernel_version 46 || return 0 + f="$DIR/f54e" + string="aaaaaa" + mknod $f c 4 0 + echo $string > $f || error +} +run_test 54e "console/tty device works in lustre ======================" + check_fstype() { grep -q $FSTYPE /proc/filesystems && return 1 modprobe $FSTYPE @@ -1998,8 +2106,7 @@ test_56() { "lfs find --recursive $DIR/d56 wrong: found $FILENUM, expected $NUMFILESx2" FILENUM=`$LFIND $DIR/d56 | grep -c obdidx` [ $FILENUM -eq $NUMFILES ] || error \ - "lfs find $DIR/d56 without --recursive wrong: found $FILENUM, - expected $NUMFILES" + "lfs find $DIR/d56 without --recursive wrong: found $FILENUM, expected $NUMFILES" echo "lfs find --recursive passed." # test lfs find with file instead of dir @@ -2017,7 +2124,7 @@ test_56() { #test lfs find with --obd $LFIND --obd wrong_uuid $DIR/d56 2>&1 | grep -q "unknown obduuid" || \ - error "lfs find --obd wrong_uuid should return error information" + error "lfs find --obd wrong_uuid should return error message" [ "$OSTCOUNT" -lt 2 ] && \ echo "skipping other lfs find --obd test" && return @@ -2026,7 +2133,7 @@ test_56() { FOUND=`$LFIND -r --obd $OBDUUID $DIR/d56 | wc -l` [ $FOUND -eq $FILENUM ] || \ error "lfs find --obd wrong: found $FOUND, expected $FILENUM" - [ `$LFIND -r -v --obd $OBDUUID $DIR/d56 | sed '/^[ ]*1[ ]/d' | \ + [ `$LFIND -r -v --obd $OBDUUID $DIR/d56 | sed '/^[ ]*1[ ]/d' |\ sed -n '/^[ ]*[0-9][0-9]*[ ]/p' | wc -l` -eq 0 ] || \ error "lfs find --obd wrong: should not show file on other obd" echo "lfs find --obd passed." @@ -2069,9 +2176,10 @@ test_57b() { $LFIND $FILE1 | grep -q "obdidx" || error "$FILE1 missing EA" $LFIND $FILEN | grep -q "obdidx" || error "$FILEN missing EA" + sleep 1 # make sure we get new statfs data MDSFREE2="`cat /proc/fs/lustre/mds/*/kbytesfree`" MDCFREE2="`cat /proc/fs/lustre/mdc/*/kbytesfree`" - if [ "$MDCFREE" != "$MDCFREE2" ]; then + if [ "$MDCFREE2" -lt "$((MDCFREE - 8))" ]; then if [ "$MDSFREE" != "$MDSFREE2" ]; then error "MDC before $MDCFREE != after $MDCFREE2" else @@ -2109,6 +2217,13 @@ test_60() { } run_test 60 "llog sanity tests run from kernel module ==========" +test_60b() { # bug 6411 + dmesg > $DIR/dmesg + LLOG_COUNT=`dmesg | grep -c llog_test` + [ $LLOG_COUNT -gt 50 ] && error "CDEBUG_LIMIT broken" || true +} +run_test 60b "limit repeated messages from CERROR/CWARN ========" + test_61() { f="$DIR/f61" dd if=/dev/zero of=$f bs=`page_size` count=1 @@ -2254,6 +2369,20 @@ test_65h() { } run_test 65h "directory stripe info inherit ======" +test_65i() { # bug6367 + $LSTRIPE $MOUNT 65536 -1 -1 +} +run_test 65i "set default striping on root directory (bug 6367)=" + +test_65j() { # bug6367 + # if we aren't already remounting for each test, do so for this test + if [ "$CLEAN" = ":" ]; then + clean || error "failed to unmount" + start || error "failed to remount" + fi +} +run_test 65j "get default striping on root directory (bug 6367)=" + # bug 2543 - update blocks count on client test_66() { COUNT=${COUNT:-8} @@ -2395,6 +2524,19 @@ run_test 72 "Test that remove suid works properly (bug5695) ====" #b_cray run_test 73 "multiple MDC requests (should not deadlock)" +test_74() { # bug 6149, 6184 + #define OBD_FAIL_LDLM_ENQUEUE_OLD_EXPORT 0x30e + # + # very important to OR with OBD_FAIL_ONCE (0x80000000) -- otherwise it + # will spin in a tight reconnection loop + sysctl -w lustre.fail_loc=0x8000030e + # get any lock + touch $DIR/f74 + sysctl -w lustre.fail_loc=0 + true +} +run_test 74 "ldlm_enqueue freed-export error path (shouldn't LBUG)" + # on the LLNL clusters, runas will still pick up root's $TMP settings, # which will not be writable for the runas user, and then you get a CVS # error message with a corrupt path string (CVS bug) and panic. @@ -2475,7 +2617,7 @@ log "cleanup: ======================================================" if [ "`mount | grep ^$NAME`" ]; then rm -rf $DIR/[Rdfs][1-9]* if [ "$I_MOUNTED" = "yes" ]; then - sh llmountcleanup.sh || error + sh llmountcleanup.sh || error "llmountcleanup failed" fi fi diff --git a/lustre/tests/sanityN.sh b/lustre/tests/sanityN.sh index 9948c81..dfe9187 100644 --- a/lustre/tests/sanityN.sh +++ b/lustre/tests/sanityN.sh @@ -419,6 +419,20 @@ test_20() { run_test 20 "test extra readahead page left in cache ====" +test_21() { # Bug 5907 + mkdir $DIR1/d21 + mount /etc $DIR1/d21 --bind # Poor man's mount. + rmdir $DIR1/d21 && error "Removed mounted directory" + rmdir $DIR2/d21 && echo "Removed mounted directory from another mountpoint, needs to be fixed" + test -d $DIR1/d21 || error "Monted directory disappeared" + umount $DIR1/d21 + test -d $DIR2/d21 || test -d $DIR1/d21 && error "Removed dir still visible after umount" + true +} + +run_test 21 " Try to remove mountpoint on another dir ====" + + log "cleanup: ======================================================" rm -rf $DIR1/[df][0-9]* $DIR1/lnk || true diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 6173ecd..f5e4509 100644 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -151,13 +151,13 @@ client_df() { } client_reconnect() { - df $MOUNT > /dev/null uname -n >> $MOUNT/recon if [ ! -z "$CLIENTS" ]; then $PDSH $CLIENTS "df $MOUNT; uname -n >> $MOUNT/recon" > /dev/null fi echo Connected clients: cat $MOUNT/recon + ls -l $MOUNT/recon > /dev/null rm $MOUNT/recon } @@ -309,12 +309,11 @@ change_active() { do_node() { HOST=$1 shift - if $VERBOSE; then - echo "CMD: $HOST $@" - $PDSH $HOST $LCTL mark "$@" > /dev/null 2>&1 || : + echo "CMD: $HOST $@" + $PDSH $HOST $LCTL mark "$@" > /dev/null 2>&1 || : fi - $PDSH $HOST "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests; cd $RPWD; sh -c \"$@\")" + $PDSH $HOST "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin; cd $RPWD; sh -c \"$@\")" } do_facet() { @@ -433,55 +432,55 @@ absolute_path() { drop_request() { # OBD_FAIL_MDS_ALL_REQUEST_NET RC=0 - do_facet mds "echo 0x123 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0x123 do_facet client "$1" || RC=$? - do_facet mds "echo 0 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0 return $RC } drop_reply() { # OBD_FAIL_MDS_ALL_REPLY_NET RC=0 - do_facet mds "echo 0x122 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0x122 do_facet client "$@" || RC=$? - do_facet mds "echo 0 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0 return $RC } drop_reint_reply() { # OBD_FAIL_MDS_REINT_NET_REP RC=0 - do_facet mds "echo 0x119 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0x119 do_facet client "$@" || RC=$? - do_facet mds "echo 0 > /proc/sys/lustre/fail_loc" + do_facet mds sysctl -w lustre.fail_loc=0 return $RC } pause_bulk() { #define OBD_FAIL_OST_BRW_PAUSE_BULK 0x214 RC=0 - do_facet ost "echo 0x214 > /proc/sys/lustre/fail_loc" + do_facet ost sysctl -w lustre.fail_loc=0x214 do_facet client "$1" || RC=$? do_facet client "sync" - do_facet ost "echo 0 > /proc/sys/lustre/fail_loc" + do_facet ost sysctl -w lustre.fail_loc=0 return $RC } drop_ldlm_cancel() { #define OBD_FAIL_LDLM_CANCEL 0x304 RC=0 - do_facet client "echo 0x304 > /proc/sys/lustre/fail_loc" + do_facet client sysctl -w lustre.fail_loc=0x304 do_facet client "$@" || RC=$? - do_facet client "echo 0 > /proc/sys/lustre/fail_loc" + do_facet client sysctl -w lustre.fail_loc=0 return $RC } drop_bl_callback() { #define OBD_FAIL_LDLM_BL_CALLBACK 0x305 RC=0 - do_facet client "echo 0x305 > /proc/sys/lustre/fail_loc" + do_facet client sysctl -w lustre.fail_loc=0x305 do_facet client "$@" || RC=$? - do_facet client "echo 0 > /proc/sys/lustre/fail_loc" + do_facet client sysctl -w lustre.fail_loc=0 return $RC } diff --git a/lustre/tests/test.c b/lustre/tests/test.c deleted file mode 100755 index d4c6bf7..0000000 --- a/lustre/tests/test.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2001 Cluster File Systems, Inc. - * - * This code is issued under the GNU General Public License. - * See the file COPYING in this distribution - */ - -#include <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <asm/statfs.h> -#include <unistd.h> -#include <linux/lustre_idl.h> - -#define LOOP_DEVICE "/dev/loop0" -#define OBD_DEVICE "/dev/obd" - -int main (int argc, char * argv[]) -{ - int fd, rc, err = -1; - struct stat stat_buf; - struct statfs stfs; - - - if (argc < 2) { - printf("syntax: %s command [argument]\n", argv[0]); - printf("Where command is one of \"setup\", \"create\", \"destroy\", or \"sync\".\n"); - exit(1); - } - if (stat(LOOP_DEVICE, &stat_buf)) { - printf("Couldn't stat(" LOOP_DEVICE ").\n"); - exit(1); - } - printf("Device: %u\n", (unsigned int) stat_buf.st_rdev); - - fd = open (OBD_DEVICE, O_RDONLY); - if (fd == -1) { - printf("Couldn't open " OBD_DEVICE ".\n"); - exit(1); - } - - if (!strcmp(argv[1], "setup")) { - rc = ioctl(fd, OBD_IOC_SETUP, &stat_buf.st_rdev); - fprintf(stderr, "rc = %d, errno = %d\n", rc, errno); - } else if (!strcmp(argv[1], "create")) { - int iter, i; - - if (argc < 3) { - printf("create requires a nonzero argument.\n"); - exit(1); - } - - iter = atoi(argv[2]); - if (iter < 1) { - printf("create requires a nonzero argument.\n"); - exit(1); - } - printf("creating %d objects...\n", iter); - - for (i = 0; i < iter; i++) { - if ((rc = ioctl(fd, OBD_IOC_CREATE, &err))) { - fprintf(stderr, "Error; aborting.\n"); - break; - } - if ((rc = ioctl(fd, OBD_IOC_DESTROY, &err))) { - fprintf(stderr, "Error; aborting.\n"); - break; - } - } - fprintf(stderr, "rc = %d, errno = %d, err = %d\n", - rc, errno, err); - } else if (!strcmp(argv[1], "sync")) { - rc = ioctl(fd, OBD_IOC_SYNC, &err); - fprintf(stderr, "rc = %d, errno = %d, err = %d\n", - rc, errno, err); - } else if (!strcmp(argv[1], "destroy")) { - int ino; - - if (argc < 3) { - printf("destroy requires a nonzero inode number.\n"); - exit(1); - } - - ino = atoi(argv[2]); - if (ino < 1) { - printf("destroy requires a nonzero inode number.\n"); - exit(1); - } - - rc = ioctl(fd, OBD_IOC_DESTROY, &ino); - fprintf(stderr, "rc = %d, errno = %d\n", rc, errno); - } else { - printf("Invalid command, run with no arguments for help.\n"); - } - close(fd); - - return 0; -} diff --git a/lustre/tests/test_brw.c b/lustre/tests/test_brw.c index ecefe92..dd94d1b 100644 --- a/lustre/tests/test_brw.c +++ b/lustre/tests/test_brw.c @@ -15,17 +15,7 @@ #include <sys/types.h> #include <sys/stat.h> -#define CERROR(fmt, arg...) fprintf(stderr, fmt, ## arg) -#ifndef __u64 -#define __u64 long long -#define cpu_to_le64(v) (v) -#define le64_to_cpu(v) (v) -#endif - -#ifndef LPU64 -#define LPU64 "%Lu" -#define LPX64 "%#Lx" -#endif +#include <liblustre.h> #define READ 1 #define WRITE 2 @@ -91,9 +81,9 @@ int main(int argc, char **argv) { int fd; char *buf; - long long count, last, offset; + loff_t count, last, offset; long pg_vec, len; - long long objid; + __u64 objid; struct stat st; int flags = 0; int cmd = 0; diff --git a/lustre/tests/uml.sh b/lustre/tests/uml.sh index 7b72020..a9480a8 100644 --- a/lustre/tests/uml.sh +++ b/lustre/tests/uml.sh @@ -15,7 +15,7 @@ OSTDEVBASE=$TMP/ost #etc OSTSIZE=${OSTSIZE:-100000} STRIPECNT=${STRIPECNT:-1} -STRIPE_BYTES=${STRIPE_BYTES:-$((1024 * 1024))} +STRIPE_BYTES=${STRIPE_BYTES:-1048576} OSDTYPE=${OSDTYPE:-obdfilter} OSTFAILOVER=${OSTFAILOVER:-} @@ -77,6 +77,13 @@ h2gm () { echo `gmnalnid -n$1` } +h2iib () { + case $1 in + client) echo '\*' ;; + *) echo $1 | sed "s/[^0-9]*//" ;; + esac +} + # create nodes echo -n "adding NET for:" for NODE in `echo $MDSNODE $OSTNODES $CLIENTS | tr -s " " "\n" | sort -u`; do diff --git a/lustre/tests/writemany.c b/lustre/tests/writemany.c index 5d4e42e..ff9a21c 100644 --- a/lustre/tests/writemany.c +++ b/lustre/tests/writemany.c @@ -25,6 +25,16 @@ char cmdname[512]; int o_abort = 0; int o_quiet = 0; +void usage(char *name) +{ + fprintf(stderr, "usage: %s [opts] <dirname> <seconds> <threads>\n", + name); + fprintf(stderr, " -q quiet\n"); + fprintf(stderr, " -a abort other children on first err\n"); + exit(1); +} + + struct kid_list_t { pid_t kid; struct kid_list_t *next; @@ -49,19 +59,26 @@ void kill_kids(void) } } +static int usr1_received; +void usr1_handler(int unused) +{ + usr1_received = 1; + kill_kids(); +} + int wait_for_threads(int live_threads) { int rc = 0; - + while (live_threads > 0) { int status; pid_t ret; - + ret = waitpid(0, &status, 0); if (ret == 0) { continue; } - + if (ret < 0) { fprintf(stderr, "%s: error: wait - %s\n", cmdname, strerror(errno)); @@ -75,7 +92,7 @@ int wait_for_threads(int live_threads) * always returns 1 (OK). See wait(2). */ int err = WEXITSTATUS(status); - if (err || WIFSIGNALED(status)) + if (err) fprintf(stderr, "%s: error: PID %d had rc=%d\n", cmdname, ret, err); @@ -100,7 +117,7 @@ int wait_for_threads(int live_threads) void print_err(char *op, char *filename, struct timeval *time, int err) { fprintf(stderr, "%s: %d.%.06d error: %s(%s): %s\n", - cmdname, (int)(time->tv_sec), (int)(time->tv_usec), op, + cmdname, (int)(time->tv_sec), (int)(time->tv_usec), op, filename, strerror(errno)); } @@ -113,11 +130,11 @@ int run_one_child(char *file, int thread, int seconds) int fd, rc = 0, rand, maxrand, len; long nfiles = 0, nbytes = 0; - if (!o_quiet) + if (!o_quiet) printf("%s: running thread #%d\n", cmdname, thread); - + srandom(thread); - /* Higher thread numbers will produce bigger random files. + /* Higher thread numbers will produce bigger random files. Thread 1 will produce only 0-len files. */ maxrand = 1; rand = thread; while (--rand) @@ -126,20 +143,25 @@ int run_one_child(char *file, int thread, int seconds) gettimeofday(&start, NULL); while(!rc) { - gettimeofday(&cur, NULL); - if (cur.tv_sec > (start.tv_sec + seconds)) + if (usr1_received) break; - sprintf(filename, "%s-%d-%ld", file, thread, nfiles); + gettimeofday(&cur, NULL); + if (seconds) { + if (cur.tv_sec > (start.tv_sec + seconds)) + break; + } + sprintf(filename, "%s-%d-%ld", file, thread, nfiles); + fd = open(filename, O_RDWR | O_CREAT, 0666); if (fd < 0) { print_err("open", filename, &cur, errno); rc = errno; break; } - - sprintf(buf, "%s %010ld %.19s.%012d\n", cmdname, + + sprintf(buf, "%s %010ld %.19s.%012d\n", cmdname, nfiles++, ctime(&cur.tv_sec), (int)cur.tv_usec); len = strlen(buf); @@ -149,10 +171,10 @@ int run_one_child(char *file, int thread, int seconds) print_err("write", filename, &cur, errno); rc = errno; break; - } + } nbytes += len; - } - + } + if (close(fd) < 0) { print_err("close", filename, &cur, errno); rc = errno; @@ -164,10 +186,10 @@ int run_one_child(char *file, int thread, int seconds) break; } } - + diff = difftime(&cur, &start); - if (!o_quiet) - printf("%s: %7ld files, %4ld MB in %.2fs (%7.2f files/s, " + if (!o_quiet) + printf("%s: %7ld files, %4ld MB in %.2fs (%7.2f files/s, " "%5.2f MB/s): rc = %d\n", cmdname, nfiles, nbytes >> 20, diff, (double)nfiles / diff, (double)nbytes/1024/1024 / diff, @@ -176,16 +198,6 @@ int run_one_child(char *file, int thread, int seconds) return rc; } -void usage(char *name) -{ - fprintf(stderr, - "usage: %s [opts] <dirname> <seconds> <threads>\n", - name); - fprintf(stderr, " -q quiet\n"); - fprintf(stderr, " -a abort other children on first err\n"); - exit(1); -} - int main(int argc, char *argv[]) { unsigned long duration; @@ -194,7 +206,7 @@ int main(int argc, char *argv[]) char *directory; int i = 1, rc = 0; - sprintf(cmdname, "%s", argv[0]); + sprintf(cmdname, "%s", argv[0]); while((i < argc) && (argv[i][0] == '-')) { switch (argv[i][1]) { @@ -226,6 +238,8 @@ int main(int argc, char *argv[]) exit(2); } + signal(SIGUSR1, usr1_handler); + for (i = 1; i <= threads; i++) { rc = fork(); if (rc < 0) { @@ -244,7 +258,7 @@ int main(int argc, char *argv[]) } } /* parent process */ - if (!o_quiet) + if (!o_quiet) printf("%s will run for %ld minutes\n", cmdname, duration/60); return (wait_for_threads(threads)); } diff --git a/lustre/utils/Lustre/lustredb.py b/lustre/utils/Lustre/lustredb.py index d60c9c1..2938400 100644 --- a/lustre/utils/Lustre/lustredb.py +++ b/lustre/utils/Lustre/lustredb.py @@ -7,20 +7,60 @@ import Lustre # XML processing and query class LustreDB: + caching_enabled = 1 + + def __init__(self): + self.lookup_uuid_cache = {} + self.lookup_name_cache = {} + self.lookup_class_cache = {} + self.lookup_val_cache = {} + self.lookup_refs_cache = {} + self.lookup_lovtgts_cache = {} + self.lookup_nid2srv_cache = {} + self.lookup_activedev_cache = {} + self.lookup_tgtdev_cache = {} + self.lookup_group_cache = {} + + self.lookup_allrefs_cache = None + self.lookup_networks_cache = None + def lookup(self, uuid): """ lookup returns a new LustreDB instance""" - return self._lookup_by_uuid(uuid) + if self.caching_enabled and self.lookup_uuid_cache.has_key(uuid): + res = self.lookup_uuid_cache[uuid] + else: + res = self._lookup_by_uuid(uuid) + if self.caching_enabled: + self.lookup_uuid_cache[uuid] = res + return res def lookup_name(self, name, class_name = ""): """ lookup returns a new LustreDB instance""" - return self._lookup_by_name(name, class_name) + if self.caching_enabled and self.lookup_name_cache.has_key((name, class_name)): + res = self.lookup_name_cache[(name, class_name)] + else: + res = self._lookup_by_name(name, class_name) + if self.caching_enabled: + self.lookup_name_cache[(name, class_name)] = res + return res def lookup_class(self, class_name): """ lookup returns a new LustreDB instance""" - return self._lookup_by_class(class_name) + if self.caching_enabled and self.lookup_class_cache.has_key(class_name): + res = self.lookup_class_cache[class_name] + else: + res = self._lookup_by_class(class_name) + if self.caching_enabled: + self.lookup_class_cache[class_name] = res + return res def get_val(self, tag, default=None): - v = self._get_val(tag) + if self.caching_enabled and self.lookup_class_cache.has_key(tag): + v = self.lookup_val_cache[tag] + else: + v = self._get_val(tag) + if self.caching_enabled: + self.lookup_val_cache[tag] = v if v: return v if default != None: @@ -31,7 +71,7 @@ class LustreDB: return self._get_class() def get_val_int(self, tag, default=0): - str = self._get_val(tag) + str = self.get_val(tag) try: if str: return int(str) @@ -42,34 +82,55 @@ class LustreDB: def get_first_ref(self, tag): """ Get the first uuidref of the type TAG. Only one is expected. Returns the uuid.""" - uuids = self._get_refs(tag) + uuids = self.get_refs(tag) if len(uuids) > 0: return uuids[0] return None def get_refs(self, tag): """ Get all the refs of type TAG. Returns list of uuids. """ - uuids = self._get_refs(tag) + if self.caching_enabled and self.lookup_refs_cache.has_key(tag): + uuids = self.lookup_refs_cache[tag] + else: + uuids = self._get_refs(tag) + if self.caching_enabled: + self.lookup_refs_cache[tag] = uuids return uuids def get_all_refs(self): """ Get all the refs. Returns list of uuids. """ - uuids = self._get_all_refs() + if self.caching_enabled and self.lookup_allrefs_cache: + uuids = self.lookup_allrefs_cache + else: + uuids = self._get_all_refs() + if self.caching_enabled: + self.lookup_allrefs_cache = uuids return uuids def get_lov_tgts(self, tag): """ Returns list of lov tgts. """ - tgts = self._get_lov_tgts(tag) + if self.caching_enabled and self.lookup_lovtgts_cache.has_key(tag): + tgts = self.lookup_lovtgts_cache[tag] + else: + tgts = self._get_lov_tgts(tag) + if self.caching_enabled: + self.lookup_lovtgts_cache[tag] = tgts return tgts def nid2server(self, nid, net_type, cluster_id): - netlist = self.lookup_class('network') - for net_db in netlist: - if (net_db.get_val('nid') == nid and - net_db.get_val('nettype') == net_type and - net_db.get_val('clusterid') == cluster_id): - return net_db - return None + if self.caching_enabled and self.lookup_nid2srv_cache.has_key((nid, net_type, cluster_id)): + res = self.lookup_nid2srv_cache[(nid, net_type, cluster_id)] + else: + netlist = self.lookup_class('network') + for net_db in netlist: + if (net_db.get_val('nid') == nid and + net_db.get_val('nettype') == net_type and + net_db.get_val('clusterid') == cluster_id): + res = net_db + break + if self.caching_enabled: + self.lookup_nid2srv_cache[(nid, net_type, cluster_id)] = res + return res # Find the target_device for target on a node # node->profiles->device_refs->target @@ -81,44 +142,68 @@ class LustreDB: # get all network uuids for this node def get_networks(self): - ret = [] - prof_list = self.get_refs('profile') - for prof_uuid in prof_list: - prof_db = self.lookup(prof_uuid) - net_list = prof_db.get_refs('network') - for net_uuid in net_list: - ret.append(net_uuid) + if self.caching_enabled and self.lookup_networks_cache: + ret = self.lookup_networks_cache + else: + ret = [] + prof_list = self.get_refs('profile') + for prof_uuid in prof_list: + prof_db = self.lookup(prof_uuid) + net_list = prof_db.get_refs('network') + for net_uuid in net_list: + ret.append(net_uuid) + if self.caching_enabled: + self.lookup_networks_cache = ret return ret def get_active_dev(self, tgtuuid): - tgt = self.lookup(tgtuuid) - tgt_dev_uuid =tgt.get_first_ref('active') + if self.caching_enabled and self.lookup_activedev_cache.has_key(tgtuuid): + tgt_dev_uuid = self.lookup_activedev_cache[tgtuuid] + else: + tgt = self.lookup(tgtuuid) + tgt_dev_uuid = tgt.get_first_ref('active') + if self.caching_enabled: + self.lookup_activedev_cache[tgtuuid] = tgt_dev_uuid return tgt_dev_uuid def get_tgt_dev(self, tgtuuid): - prof_list = self.get_refs('profile') - for prof_uuid in prof_list: - prof_db = self.lookup(prof_uuid) - if not prof_db: - panic("profile:", profile, "not found.") - for ref_class, ref_uuid in prof_db.get_all_refs(): - if ref_class in ('osd', 'mdsdev'): - devdb = self.lookup(ref_uuid) - uuid = devdb.get_first_ref('target') - if tgtuuid == uuid: - return ref_uuid - return None + if self.caching_enabled and self.lookup_tgtdev_cache.has_key(tgtuuid): + res = self.lookup_tgtdev_cache[tgtuuid] + else: + prof_list = self.get_refs('profile') + res = None + for prof_uuid in prof_list: + prof_db = self.lookup(prof_uuid) + if not prof_db: + panic("profile:", profile, "not found.") + for ref_class, ref_uuid in prof_db.get_all_refs(): + if ref_class in ('osd', 'mdsdev'): + devdb = self.lookup(ref_uuid) + uuid = devdb.get_first_ref('target') + if tgtuuid == uuid: + res = ref_uuid + break + if not res is None: + break + if self.caching_enabled: + self.lookup_tgtdev_cache[tgtuuid] = res + return res def get_group(self, group): - ret = [] - devs = self.lookup_class('mds') - for tgt in devs: - if tgt.get_val('group', "") == group: - ret.append(tgt.getUUID()) - devs = self.lookup_class('ost') - for tgt in devs: - if tgt.get_val('group', "") == group: - ret.append(tgt.getUUID()) + if self.caching_enabled and self.lookup_group_cache.has_key(group): + ret = self.lookup_group_cache[group] + else: + ret = [] + devs = self.lookup_class('mds') + for tgt in devs: + if tgt.get_val('group', tgt.get_value('name')) == group: + ret.append(tgt.getUUID()) + devs = self.lookup_class('ost') + for tgt in devs: + if tgt.get_val('group', tgt.get_value('name')) == group: + ret.append(tgt.getUUID()) + if self.caching_enabled: + self.lookup_group_cache[group] = ret return ret # Change the current active device for a target @@ -130,6 +215,8 @@ class LustreDB: class LustreDB_XML(LustreDB): def __init__(self, dom, root_node): + LustreDB.__init__(self) + # init xmlfile self.dom_node = dom self.root_node = root_node @@ -306,6 +393,8 @@ class LustreDB_LDAP(LustreDB): user = "cn=Manager, fs=lustre", pw = "" ): + LustreDB.__init__(self) + self._name = name self._attrs = attrs self._base = base diff --git a/lustre/utils/Makefile.am b/lustre/utils/Makefile.am index 47b6d8f..20c1542 100644 --- a/lustre/utils/Makefile.am +++ b/lustre/utils/Makefile.am @@ -3,8 +3,9 @@ SUBDIRS = Lustre AM_CFLAGS=$(LLCFLAGS) -AM_CPPFLAGS=$(LLCPPFLAGS) -AM_LDFLAGS := -L$(top_builddir)/portals/utils +AM_CPPFLAGS=$(LLCPPFLAGS) -DLUSTRE_UTILS=1 + +LIBPTLCTL := $(top_builddir)/portals/utils/libptlctl.a sbin_scripts = lconf lmc llanalyze llstat.pl llobdstat.pl lactive \ load_ldap.sh lrun lwizard @@ -19,20 +20,31 @@ sbin_SCRIPTS = $(sbin_scripts) bin_SCRIPTS = $(bin_scripts) endif # UTILS -lctl_LDADD := $(LIBREADLINE) -lptlctl -lfs_LDADD := $(LIBREADLINE) parser.o liblustreapi.a -lptlctl obd.o -lload_LDADD := $(LIBREADLINE) -lptlctl +lctl_LDADD := $(LIBREADLINE) $(LIBPTLCTL) +lctl_DEPENDENCIES := $(LIBPTLCTL) + +lfs_LDADD := $(LIBREADLINE) liblustreapi.a $(LIBPTLCTL) +lfs_DEPENDENCIES := $(LIBPTLCTL) liblustreapi.a + +lload_LDADD := $(LIBREADLINE) $(LIBPTLCTL) +lload_DEPENDENCIES := $(LIBPTLCTL) + liblustreapi_a_SOURCES = liblustreapi.c + wirecheck_SOURCES = wirecheck.c wirecheck_CPPFLAGS = -DCC="\"$(CC)\"" wiretest_SOURCES = wiretest.c + lctl_SOURCES = parser.c obd.c lustre_cfg.c lctl.c parser.h obdctl.h + lload_SOURCES = lload.c obdio_SOURCES = obdio.c obdiolib.c obdiolib.h obdbarrier_SOURCES = obdbarrier.c obdiolib.c obdiolib.h -lfs_SOURCES = lfs.c +lfs_SOURCES = lfs.c parser.c obd.c + llmount_SOURCES = llmount.c -llmount_LDADD = $(LIBREADLINE) -lptlctl +llmount_LDADD = $(LIBREADLINE) $(LIBPTLCTL) +llmount_DEPENDENCIES := $(LIBPTLCTL) EXTRA_DIST = $(bin_scripts) $(sbin_scripts) diff --git a/lustre/utils/l_getgroups.c b/lustre/utils/l_getgroups.c new file mode 100644 index 0000000..8909422 --- /dev/null +++ b/lustre/utils/l_getgroups.c @@ -0,0 +1,104 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * Copyright (C) 2004 Cluster File Systems, Inc. + * + * This file is part of Lustre, http://www.lustre.org. + * + * Lustre is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * Lustre is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Lustre; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> +#include <lustre/lustre_user.h> + +int get_groups_local(struct mds_grp_downcall_data **grp) +{ + struct mds_grp_downcall_data *param; + int i, maxgroups, size; + struct passwd *pw; + struct group *gr; + + pw = getpwuid((*grp)->mgd_uid); + if (!pw) { + (*grp)->mgd_err = -errno; + return sizeof(*param); + } + + maxgroups = sysconf(_SC_NGROUPS_MAX); + size = offsetof(struct mds_grp_downcall_data, mgd_groups[maxgroups]); + param = malloc(size); + if (param == NULL) { + (*grp)->mgd_err = -ENOMEM; + return sizeof(*param); + } + + memcpy(param, *grp, sizeof(*param)); + *grp = param; + while ((gr = getgrent())) { + if (!gr->gr_mem) + continue; + for (i = 0; gr->gr_mem[i]; i++) { + if (strcmp(gr->gr_mem[i], pw->pw_name) == 0) { + param->mgd_groups[param->mgd_ngroups++] = + gr->gr_gid; + break; + } + } + if (param->mgd_ngroups == maxgroups) + break; + } + endgrent(); + + return size; +} + +/* Note that we need to make the downcall regardless of error, so that the + * MDS doesn't continue to wait on the upcall. */ +int main(int argc, char **argv) +{ + int fd, rc, size; + struct mds_grp_downcall_data sparam = { MDS_GRP_DOWNCALL_MAGIC }; + struct mds_grp_downcall_data *param = &sparam; + char pathname[1024]; + + if (argc != 3) { + printf("bad parameter\n"); + return -1; + } + + snprintf(pathname, 1024, "/proc/fs/lustre/mds/%s/group_info", argv[1]); + param->mgd_uid = atoi(argv[2]); + + fd = open(pathname, O_WRONLY); + if (fd < 0) { + printf("can't open device %s\n", pathname); + return -1; + } + + size = get_groups_local(¶m); + + rc = write(fd, param, size); + + close(fd); + return rc; +} diff --git a/lustre/utils/lconf b/lustre/utils/lconf index 33a19b6..408bb76 100755 --- a/lustre/utils/lconf +++ b/lustre/utils/lconf @@ -37,7 +37,7 @@ if sys.version[0] == '1': else: from fcntl import F_GETFL, F_SETFL -PYMOD_DIR = ["/usr/lib/lustre/python", "/usr/lib64/lustre/python"] +PYMOD_DIR = ["/usr/lib64/lustre/python", "/usr/lib/lustre/python"] def development_mode(): base = os.path.dirname(sys.argv[0]) @@ -153,8 +153,10 @@ def logall(msgs): print string.strip(s) def debug(*args): + # apparently, (non)execution of the following line affects mds device + # startup order (e.g. two mds's using loopback devices), so always do it. + msg = string.join(map(str,args)) if config.verbose: - msg = string.join(map(str,args)) print msg # ack, python's builtin int() does not support '0x123' syntax. @@ -290,6 +292,8 @@ class AcceptorHandler(DaemonHandler): self.port = port self.net_type = net_type self.flags = '' + if config.allow_unprivileged_port: + self.flags = '-p' def pidfile(self): return "/var/run/%s-%d.pid" % (self.command, self.port) @@ -483,7 +487,7 @@ class LCTLInterface: def add_peer(self, net_type, nid, hostaddr, port): """ noop """ -# if net_type in ('tcp','openib','ra') and not config.lctl_dump: +# if net_type in ('tcp','openib','ra','cray_kern_nal') and not config.lctl_dump: # cmds = """ # network %s # add_peer %s %s %d @@ -507,10 +511,49 @@ class LCTLInterface: def connect(self, srv): self.add_uuid(srv.net_type, srv.nid_uuid, srv.nid) -# if srv.net_type in ('tcp','openib','iib','vib','ra') and not config.lctl_dump: -# if srv.hostaddr[0]: -# hostaddr = string.split(srv.hostaddr[0], '/')[0] -# self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port) +# if config.lctl_dump: +# return +# +# if srv.net_type in ('tcp',): +# host = socket.gethostname() +# node_list = [] +# if config.node: +# node_list.append(config.node) +# else: +# if len(host) > 0: +# node_list.append(host) +# node_list.append('localhost') +# +# node_db = None +# for h in node_list: # we are quite sure we can find the node_db +# node_db = toplustreDB.lookup_name(h, 'node') +# if node_db: +# break +# +# hostaddr = None +# for netuuid in node_db.get_networks(): +# localnet = toplustreDB.lookup(netuuid) +# localnet = Network(localnet) +# if localnet.net_type != 'tcp': +# continue # only tcp understands multiple hostaddrs +# +# # always true for tcp network +# if localnet.hostaddr[0] and srv.hostaddr[0]: +# for lnet in localnet.hostaddr: +# for pnet in srv.hostaddr: +# if srv.netmatch(lnet, pnet) != 0: +# hostaddr = string.split(pnet, '/')[0] +# #find one is enough, should break the top-most loop +# break +# if hostaddr: break +# else: # can't find a match +# hostaddr = string.split(srv.hostaddr[0], '/')[0] +# break +# +# self.add_peer(srv.net_type, srv.nid, hostaddr, srv.port) +# +# if srv.net_type in ('openib','iib','vib','ra'): +# self.add_peer(srv.net_type, srv.nid, srv.hostaddr[0], srv.port) # Recover a device def recover(self, dev_name, new_conn): @@ -629,7 +672,7 @@ class LCTLInterface: def abort_recovery(self, name): cmds = """ ignore_errors - device %s + device $%s abort_recovery quit""" % (name) self.run(cmds) @@ -918,7 +961,7 @@ def mkfs(dev, devsize, fstype, jsize, isize, mkfsoptions, isblock=1): panic("Unable to build fs:", dev, string.join(out)) # enable hash tree indexing on fsswe if fstype in ('ext3', 'extN', 'ldiskfs'): - htree = 'echo "feature FEATURE_C5" | debugfs -w' + htree = 'tune2fs -O dir_index' (ret, out) = run (htree, dev) if ret: panic("Unable to enable htree:", dev) @@ -933,7 +976,7 @@ def loop_base(): loop='/dev/loop' return loop -# find loop device assigned to thefile +# find loop device assigned to the file def find_loop(file): loop = loop_base() for n in xrange(0, MAX_LOOP_DEVICES): @@ -1045,15 +1088,17 @@ def sys_get_local_nid(net_type, wildcard, cluster_id): # don't need a real nid for config log - client will replace (bug5619) if config.record: local = "54321" - elif net_type in ('tcp','openib','iib','vib','ra'): + elif net_type in ('tcp','openib','iib','vib','ra','cray_kern_nal'): if ':' in wildcard: iface, star = string.split(wildcard, ':') local = if2addr(iface) - if not local: - panic("unable to determine ip for:", wildcard) + elif net_type == 'vib': + local = if2addr('ipoib0') else: host = socket.gethostname() local = socket.gethostbyname(host) + if not local: + panic("unable to determine ip for:", wildcard) elif net_type == 'elan': # awk '/NodeId/ { print $2 }' 'sys_get_elan_position_file()' f = sys_get_elan_position_file() @@ -1093,18 +1138,7 @@ def sys_get_local_nid(net_type, wildcard, cluster_id): def sys_get_branch(): """Returns kernel release""" - try: - fp = open('/proc/sys/kernel/osrelease') - lines = fp.readlines() - fp.close() - - for l in lines: - version = string.split(l) - a = string.split(version[0], '.') - return a[0] + '.' + a[1] - except IOError, e: - log(e) - return "" + return os.uname()[2][:3] def mod_loaded(modname): """Check if a module is already loaded. Look in /proc/modules for it.""" @@ -1397,6 +1431,65 @@ class Network(Module): ip = string.split(hostaddr, '/')[0] lctl.del_interface(self.net_type, ip) + def my_inet_aton(self, net): + split = net.split('.') + if len(split) != 4: + raise ValueError, "Invalid IPv4 address %s" % net + + naddr = 0 + i = 0 + for n in split: + try: + naddr = naddr + int(n) * (256 ** (3-i)) + except: + raise ValueError, "Invalid IPv4 address %s" % net + i = i + 1 + return naddr + + def tointAddr(self, net): + """convert a net address/mask into (numeric-address, bitmap-mask)""" + try: + addr, mask = string.split(net, '/') + except: + addr = net + mask = 24 #eeb told me that kernel uses this value by default + + try: + mask = int(mask) + assert(mask >= 1 and mask <= 32) + mask = bitmap_32(mask) + except: + try: + mask = self.my_inet_aton(mask) + except: + raise ValueError("Invalid netmask %s" % str(mask)) + + try: + addr = socket.gethostbyname(addr) + naddr = self.my_inet_aton(addr) + except: + raise ValueError("Invalid host %s" % addr) + + return (naddr, mask) + + def netmatch(self, net1, net2): + # XXX this is only valid for IPv4 address + try: + addr1, mask1 = self.tointAddr(net1) + addr2, mask2 = self.tointAddr(net2) + except: + return 0 + + if addr1 & mask1 == addr2 & mask2: + return 1 + return 0 + +def bitmap_32(n): + """n should be in [1, 32]""" + if n < 0 or n > 32: + raise ValueError("A number between 1 and 32 is expected. (not %d)" % n) + return (-1) << (32-n) + class RouteTable(Module): def __init__(self,db): Module.__init__(self, 'ROUTES', db) @@ -1507,11 +1600,12 @@ class LOV(Module): self.stripe_off = self.db.get_val_int('stripeoffset', 0) self.pattern = self.db.get_val_int('stripepattern', 0) self.devlist = [] - self.stripe_cnt = 0 + self.stripe_cnt = self.db.get_val_int('stripecount', 1) self.osclist = [] self.desc_uuid = self.uuid self.uuid = generate_client_uuid(self.name) self.fs_name = fs_name + # settings below here won't be seen by the MDSDEV code! if config_only: self.config_only = 1 return @@ -1542,7 +1636,7 @@ class LOV(Module): index = index + 1 if self.osclist == []: panic('No OSCs configured for LOV') - self.stripe_cnt = self.db.get_val_int('stripecount', len(self.devlist)) + debug('dbg LOV __init__:', self.osclist, self.devlist, self.stripe_cnt) def prepare(self): debug('dbg LOV prepare') @@ -1570,13 +1664,10 @@ class LOV(Module): lctl.lov_add_obd(self.name, self.uuid, target_uuid, index, gen) def cleanup(self): - for (osc, index, gen, active) in self.osclist: - target_uuid = osc.target_uuid - if is_prepared(osc.name): - lctl.lov_del_obd(self.name, self.uuid, target_uuid, index, gen) - osc.cleanup() if is_prepared(self.name): Module.cleanup(self) + for (osc, index, gen, active) in self.osclist: + osc.cleanup() if self.config_only: panic("Can't clean up config_only LOV ", self.name) @@ -1603,7 +1694,13 @@ class MDSDEV(Module): self.devpath = self.db.get_val('devpath','') self.size = self.db.get_val_int('devsize', 0) self.journal_size = self.db.get_val_int('journalsize', 0) + self.fstype = self.db.get_val('fstype', '') + if sys_get_branch() == '2.4' and self.fstype == 'ldiskfs': + self.fstype = 'ext3' + elif sys_get_branch() == '2.6' and self.fstype == 'ext3': + self.fstype = 'ldiskfs' + self.nspath = self.db.get_val('nspath', '') self.mkfsoptions = '-i 4096 ' + self.db.get_val('mkfsoptions', '') self.mountfsoptions = self.db.get_val('mountfsoptions', '') @@ -1625,7 +1722,7 @@ class MDSDEV(Module): self.active = 1 else: self.active = 0 - if self.active and config.group and config.group != mds.get_val('group'): + if self.active and config.group and config.group != mds.get_val('group', mds.get_val('name')): self.active = 0 self.inode_size = self.db.get_val_int('inodesize', 0) @@ -1644,17 +1741,19 @@ class MDSDEV(Module): if (lov.stripe_cnt > 0): stripe_count = lov.stripe_cnt else: - stripe_count = len(lov.devlist) + stripe_count = 1 if stripe_count > 77: self.inode_size = 4096 elif stripe_count > 34: self.inode_size = 2048 elif stripe_count > 13: self.inode_size = 1024 - elif stripe_count > 2: - self.inode_size = 512 + #elif stripe_count < 3: + # self.inode_size = 256 else: - self.inode_size = 256 + self.inode_size = 512 + + debug('stripe_count %d, inode_size %d', stripe_count, self.inode_size) self.target_dev_uuid = self.uuid self.uuid = target_uuid @@ -1776,7 +1875,7 @@ class MDSDEV(Module): noexec_opt = ('', '-n') ret, out = run (sys.argv[0], noexec_opt[old_noexec == 1], - " -v --record --nomod", + " --record --nomod", "--record_log", client_name, "--record_device", self.name, "--node", client_name, @@ -1865,9 +1964,19 @@ class OSD(Module): self.size = self.db.get_val_int('devsize', 0) self.journal_size = self.db.get_val_int('journalsize', 0) self.inode_size = self.db.get_val_int('inodesize', 0) - self.mkfsoptions = '-i 16384 ' + self.db.get_val('mkfsoptions', '') + self.mkfsoptions = self.db.get_val('mkfsoptions', '') + # Allocate fewer inodes on large OST devices. Most filesystems + # can be much more aggressive than this, but by default we can't. + if self.size > 1000000: + self.mkfsoptions = '-i 16384 ' + self.mkfsoptions self.mountfsoptions = self.db.get_val('mountfsoptions', '') + self.fstype = self.db.get_val('fstype', '') + if sys_get_branch() == '2.4' and self.fstype == 'ldiskfs': + self.fstype = 'ext3' + elif sys_get_branch() == '2.6' and self.fstype == 'ext3': + self.fstype = 'ldiskfs' + self.nspath = self.db.get_val('nspath', '') target_uuid = self.db.get_first_ref('target') ost = self.db.lookup(target_uuid) @@ -1885,7 +1994,7 @@ class OSD(Module): self.active = 1 else: self.active = 0 - if self.active and config.group and config.group != ost.get_val('group'): + if self.active and config.group and config.group != ost.get_val('group', ost.get_val('name')): self.active = 0 self.target_dev_uuid = self.uuid @@ -1999,7 +2108,7 @@ class Client(Module): self.target_name = tgtdb.getName() self.target_uuid = tgtdb.getUUID() self.db = tgtdb - self.backup_targets = [] + self.backup_targets = [] self.tgt_dev_uuid = get_active_target(tgtdb) if not self.tgt_dev_uuid: @@ -2982,6 +3091,7 @@ lconf_options = [ ('subsystem', "Set the portals debug subsystem", PARAM), ('gdb_script', "Fullname of gdb debug script", PARAM, default_gdb_script()), ('debug_path', "Path to save debug dumps", PARAM, default_debug_path()), + ('allow_unprivileged_port', "Allow connections from unprivileged ports"), # Client recovery options ('recover', "Recover a device"), ('group', "The group of devices to configure or cleanup", PARAM), diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index 35b123f..61f1579 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -855,16 +855,16 @@ static void print_quota(char *mnt, char *name, struct if_quotactl *qctl) printf("%15s", mnt); if (bover) diff2str(dqb->dqb_btime, timebuf, now); - sprintf(numbuf[0], "%llu", toqb(dqb->dqb_curspace)); - sprintf(numbuf[1], "%llu", dqb->dqb_bsoftlimit); - sprintf(numbuf[2], "%llu", dqb->dqb_bhardlimit); + sprintf(numbuf[0], LPU64, toqb(dqb->dqb_curspace)); + sprintf(numbuf[1], LPU64, dqb->dqb_bsoftlimit); + sprintf(numbuf[2], LPU64, dqb->dqb_bhardlimit); printf(" %7s%c %6s %7s %7s", numbuf[0], bover ? '*' : ' ', numbuf[1], numbuf[2], bover > 1 ? timebuf : ""); if (iover) diff2str(dqb->dqb_itime, timebuf, now); - sprintf(numbuf[0], "%llu", dqb->dqb_curinodes); - sprintf(numbuf[1], "%llu", dqb->dqb_isoftlimit); - sprintf(numbuf[2], "%llu", dqb->dqb_ihardlimit); + sprintf(numbuf[0], LPU64, dqb->dqb_curinodes); + sprintf(numbuf[1], LPU64, dqb->dqb_isoftlimit); + sprintf(numbuf[2], LPU64, dqb->dqb_ihardlimit); printf(" %7s%c %6s %7s %7s\n", numbuf[0], iover ? '*' : ' ', numbuf[1], numbuf[2], iover > 1 ? timebuf : ""); } diff --git a/lustre/utils/liblustreapi.c b/lustre/utils/liblustreapi.c index f84d9fb..8dffe33 100644 --- a/lustre/utils/liblustreapi.c +++ b/lustre/utils/liblustreapi.c @@ -40,8 +40,16 @@ #include <sys/stat.h> #include <sys/types.h> #include <sys/syscall.h> +#ifdef HAVE_LINUX_TYPES_H #include <linux/types.h> +#else +#include "types.h" +#endif +#ifdef HAVE_LINUX_UNISTD_H #include <linux/unistd.h> +#else +#include <unistd.h> +#endif #include <portals/ptlctl.h> @@ -84,25 +92,25 @@ int llapi_file_create(char *name, long stripe_size, int stripe_offset, /* 64 KB is the largest common page size I'm aware of (on ia64), but * check the local page size just in case. */ - page_size = 65536; + page_size = LOV_MIN_STRIPE_SIZE; if (getpagesize() > page_size) { page_size = getpagesize(); - fprintf(stderr, "WARNING: your page size (%d) is larger than " - "expected.\n", page_size); + fprintf(stderr, "WARNING: your page size (%u) is larger than " + "expected (%u).\n", page_size, LOV_MIN_STRIPE_SIZE); } - if ((stripe_size < 0 || stripe_size % 65536) && + if ((stripe_size < 0 || (stripe_size & (LOV_MIN_STRIPE_SIZE - 1))) && !(isdir && stripe_size == -1)) { rc = -EINVAL; err_msg("error: stripe_size must be an even " "multiple of %d bytes.\n", page_size); goto out; } - if (stripe_offset < -1 || stripe_offset > 65534) { + if (stripe_offset < -1 || stripe_offset > LOV_MAX_STRIPE_COUNT) { errno = rc = -EINVAL; err_msg("error: bad stripe offset %d\n", stripe_offset); goto out; } - if (stripe_count < -1 || stripe_count > 65534) { + if (stripe_count < -1 || stripe_count > LOV_MAX_STRIPE_COUNT) { errno = rc = -EINVAL; err_msg("error: bad stripe count %d\n", stripe_count); goto out; @@ -174,8 +182,7 @@ struct find_param { struct find_param *param); }; -/* XXX Max obds per lov currently hardcoded to 1000 in lov/lov_obd.c */ -#define MAX_LOV_UUID_COUNT 1000 +#define MAX_LOV_UUID_COUNT max(LOV_MAX_STRIPE_COUNT, 1000) #define OBD_NOT_FOUND (-1) static int prepare_find(struct find_param *param) @@ -210,7 +217,7 @@ int llapi_lov_get_uuids(int fd, struct obd_uuid *uuidp, int *ost_count) __u32 *obdgens; max_ost_count = (OBD_MAX_IOCTL_BUFFER - size_round(sizeof(data)) - - size_round(sizeof(desc))) / + size_round(sizeof(desc))) / (sizeof(*uuidp) + sizeof(*obdgens)); if (max_ost_count > *ost_count) max_ost_count = *ost_count; @@ -259,38 +266,58 @@ out: static int setup_obd_uuids(DIR *dir, char *dname, struct find_param *param) { - struct obd_uuid uuids[1024], *uuidp; - int obdcount = 1024; - int rc, i; + char uuid[sizeof(struct obd_uuid)]; + char buf[1024]; + FILE *fp; + int rc = 0, index; param->got_uuids = 1; - rc = llapi_lov_get_uuids(dirfd(dir), uuids, &obdcount); - if (rc != 0) - return (param->obduuid ? rc : 0); + /* Get the lov name */ + rc = ioctl(dirfd(dir), OBD_IOC_GETNAME, (void *)uuid); + if (rc) { + fprintf(stderr, "error: can't get lov name: %s\n", + strerror(rc = errno)); + return rc; + } - if (obdcount == 0) - return 0; + /* Now get the ost uuids from /proc */ + snprintf(buf, sizeof(buf), "/proc/fs/lustre/lov/%s/target_obd", + uuid); + fp = fopen(buf, "r"); + if (fp == NULL) { + fprintf(stderr, "error: %s opening %s\n", + strerror(rc = errno), buf); + return rc; + } - if (param->obduuid) { - for (i = 0, uuidp = uuids; i < obdcount; i++, uuidp++) { - if (strncmp(param->obduuid->uuid, uuidp->uuid, - sizeof(*uuidp)) == 0) { - param->obdindex = i; + if (!param->obduuid && !param->quiet) + printf("OBDS:\n"); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (sscanf(buf, "%d: %s", &index, uuid) < 2) + break; + + if (param->obduuid) { + if (strncmp(param->obduuid->uuid, uuid, + sizeof(uuid)) == 0) { + param->obdindex = index; break; } + } else if (!param->quiet) { + /* Print everything */ + printf("%s", buf); } - if (param->obdindex == OBD_NOT_FOUND) { - printf("unknown obduuid: %s\n", param->obduuid->uuid); - return EINVAL; - } - } else if (!param->quiet) { - printf("OBDS:\n"); - for (i = 0, uuidp = uuids; i < obdcount; i++, uuidp++) - printf("%4d: %s\n", i, uuidp->uuid); } - return 0; + fclose(fp); + + if (param->obduuid && (param->obdindex == OBD_NOT_FOUND)) { + printf("unknown obduuid: %s\n", param->obduuid->uuid); + rc = EINVAL; + } + + return (rc); } void lov_dump_user_lmm_v1(struct lov_user_md_v1 *lum, char *dname, char *fname, @@ -436,7 +463,8 @@ static int find_process_file(DIR *dir, char *dname, char *fname, /* add fname to directory list; */ rc = errno; } else { - err_msg("IOC_MDC_GETSTRIPE ioctl failed"); + err_msg("IOC_MDC_GETSTRIPE ioctl failed for '%s/%s'", + dname, fname); rc = errno; } return rc; diff --git a/lustre/utils/llmount.c b/lustre/utils/llmount.c index ab6806b..9e6a135 100644 --- a/lustre/utils/llmount.c +++ b/lustre/utils/llmount.c @@ -26,11 +26,13 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> +#include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/mount.h> #include <mntent.h> #include <getopt.h> +#include <sys/utsname.h> #include "obdctl.h" #include <portals/ptlctl.h> @@ -129,6 +131,7 @@ print_options(struct lustre_mount_data *lmd) printf("nid: %s\n", libcfs_nid2str(lmd->lmd_nid)); printf("mds: %s\n", lmd->lmd_mds); printf("profile: %s\n", lmd->lmd_profile); + return 0; } @@ -225,12 +228,12 @@ build_data(char *source, char *options, struct lustre_mount_data *lmd, int rc; if (lmd_bad_magic(lmd)) - return -EINVAL; + return 4; if (strlen(source) >= sizeof(buf)) { fprintf(stderr, "%s: nid:/mds/profile argument too long\n", progname); - return -EINVAL; + return 1; } strcpy(buf, source); if ((s = strchr(buf, ':'))) { diff --git a/lustre/utils/lrun b/lustre/utils/lrun index 56d3d04..e832dc3 100755 --- a/lustre/utils/lrun +++ b/lustre/utils/lrun @@ -3,11 +3,15 @@ LIBLUSTRE_MOUNT_POINT=${LIBLUSTRE_MOUNT_POINT:-"/mnt/lustre"} LIBLUSTRE_MOUNT_TARGET=${LIBLUSTRE_MOUNT_TARGET:-"TARGET_NOT_SET"} LIBLUSTRE_DUMPFILE=${LIBLUSTRE_DUMPFILE:-"/tmp/DUMP_FILE"} +LIBLUSTRE_DEBUG_MASK=${LIBLUSTRE_DEBUG_MASK:-"0"} +LIBLUSTRE_DEBUG_SUBSYS=${LIBLUSTRE_DEBUG_SUBSYS:-"0"} LD_PRELOAD=${LD_PRELOAD:-"/usr/lib/liblustre.so"} export LIBLUSTRE_MOUNT_POINT export LIBLUSTRE_MOUNT_TARGET export LIBLUSTRE_DUMPFILE +export LIBLUSTRE_DEBUG_MASK +export LIBLUSTRE_DEBUG_SUBSYS export LD_PRELOAD exec $@ diff --git a/lustre/utils/lustre_cfg.c b/lustre/utils/lustre_cfg.c index 16c6a20..35543f0 100644 --- a/lustre/utils/lustre_cfg.c +++ b/lustre/utils/lustre_cfg.c @@ -370,8 +370,10 @@ int jt_lcfg_lov_setup(int argc, char **argv) } if (desc.ld_default_stripe_size < 4096) { fprintf(stderr, - "error: %s: default stripe size "LPU64" too small\n", - jt_cmdname(argv[0]), desc.ld_default_stripe_size); + "error: %s: default stripe size "LPU64" smaller than " + "minimum %u\n", + jt_cmdname(argv[0]), desc.ld_default_stripe_size, + LOV_MIN_STRIPE_SIZE); return -EINVAL; } else if ((long)desc.ld_default_stripe_size < desc.ld_default_stripe_size) { diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index 32a6f34..39e4249 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -48,7 +48,9 @@ #include <errno.h> #include <string.h> +#ifdef HAVE_ASM_PAGE_H #include <asm/page.h> /* needed for PAGE_SIZE - rread */ +#endif #include <linux/obd_class.h> #include <portals/ptlctl.h> @@ -83,7 +85,7 @@ static int max = sizeof(rawbuf); static int thread; static int nthreads; -static uint32_t cur_device = MAX_OBD_DEVICES; +static int cur_device = MAX_OBD_DEVICES; union lsm_buffer { char space [4096]; @@ -1397,11 +1399,11 @@ int jt_obd_test_brw(int argc, char **argv) case 'r': repeat_offset = 1; break; - + case 'x': verify = 0; break; - + default: fprintf (stderr, "Can't parse cmd '%s'\n", argv[2]); @@ -1490,7 +1492,7 @@ int jt_obd_test_brw(int argc, char **argv) len = pages * getpagesize(); thr_offset = offset_pages * getpagesize(); stride = len; - + if (thread) { pthread_mutex_lock (&shared_data->mutex); if (nthr_per_obj != 0) { @@ -2011,14 +2013,14 @@ int jt_llog_catlist(int argc, char **argv) IOC_INIT(data); data.ioc_inllen1 = max - size_round(sizeof(data)); IOC_PACK(argv[0], data); - + rc = l_ioctl(OBD_DEV_ID, OBD_IOC_CATLOGLIST, buf); - if (rc == 0) + if (rc == 0) fprintf(stdout, "%s", ((struct obd_ioctl_data*)buf)->ioc_bulk); else - fprintf(stderr, "OBD_IOC_CATLOGLIST failed: %s\n", + fprintf(stderr, "OBD_IOC_CATLOGLIST failed: %s\n", strerror(errno)); - + return rc; } diff --git a/lustre/utils/parser.c b/lustre/utils/parser.c index 26f66d8..9c23e77 100644 --- a/lustre/utils/parser.c +++ b/lustre/utils/parser.c @@ -341,16 +341,46 @@ int init_input() #define add_history(s) char * readline(char * prompt) { - char line[2048]; - int n = 0; + int size = 2048; + char *line = malloc(size); + char *ptr = line; + int c; + + if (line == NULL) + return NULL; if (prompt) printf ("%s", prompt); - if (fgets(line, sizeof(line), stdin) == NULL) - return (NULL); - n = strlen(line); - if (n && line[n-1] == '\n') - line[n-1] = '\0'; - return strdup(line); + + while (1) { + if ((c = fgetc(stdin)) != EOF) { + if (c == '\n') + goto out; + *ptr++ = c; + + if (ptr - line >= size - 1) { + char *tmp; + + size *= 2; + tmp = malloc(size); + if (tmp == NULL) + goto outfree; + memcpy(tmp, line, ptr - line); + ptr = tmp + (ptr - line); + free(line); + line = tmp; + } + } else { + if (ferror(stdin)) + goto outfree; + goto out; + } + } +out: + *ptr = 0; + return line; +outfree: + free(line); + return NULL; } #endif diff --git a/lustre/utils/wirecheck.c b/lustre/utils/wirecheck.c index 905fd69..70de0ad 100644 --- a/lustre/utils/wirecheck.c +++ b/lustre/utils/wirecheck.c @@ -581,6 +581,7 @@ check_llog_logid(void) CHECK_VALUE(MDS_UNLINK_REC); CHECK_VALUE(MDS_SETATTR_REC); CHECK_VALUE(OBD_CFG_REC); + CHECK_VALUE(PTL_CFG_REC); CHECK_VALUE(LLOG_GEN_REC); CHECK_VALUE(LLOG_HDR_MAGIC); CHECK_VALUE(LLOG_LOGID_MAGIC); diff --git a/lustre/utils/wiretest.c b/lustre/utils/wiretest.c index 694184a..f802c1a 100644 --- a/lustre/utils/wiretest.c +++ b/lustre/utils/wiretest.c @@ -1108,7 +1108,7 @@ void lustre_assert_wire_constants(void) (long long)(int)sizeof(((struct ldlm_res_id *)0)->name[4])); /* Checks for struct ldlm_extent */ - LASSERTF((int)sizeof(struct ldlm_extent) == 16, " found %lld\n", + LASSERTF((int)sizeof(struct ldlm_extent) == 24, " found %lld\n", (long long)(int)sizeof(struct ldlm_extent)); LASSERTF((int)offsetof(struct ldlm_extent, start) == 0, " found %lld\n", (long long)(int)offsetof(struct ldlm_extent, start)); @@ -1332,6 +1332,8 @@ void lustre_assert_wire_constants(void) (long long)MDS_SETATTR_REC); LASSERTF(OBD_CFG_REC == 274857984, " found %lld\n", (long long)OBD_CFG_REC); + LASSERTF(PTL_CFG_REC == 274923520, " found %lld\n", + (long long)PTL_CFG_REC); LASSERTF(LLOG_GEN_REC == 274989056, " found %lld\n", (long long)LLOG_GEN_REC); LASSERTF(LLOG_HDR_MAGIC == 275010873, " found %lld\n", -- 1.8.3.1