Date: Thu, 09 Oct 2008 17:11:31 +1100
From: Donald Douwsma <donaldd@sgi.com>
Subject: DMAPI Source 
Patch-mainline: ?
References: bnc#450658

Acked-by: Jan Kara <jack@suse.cz>

---
 fs/dmapi/Makefile          |   53 +
 fs/dmapi/Status            |  128 +++
 fs/dmapi/dmapi.h           | 1086 ++++++++++++++++++++++++++
 fs/dmapi/dmapi_attr.c      |   93 ++
 fs/dmapi/dmapi_bulkattr.c  |  170 ++++
 fs/dmapi/dmapi_config.c    |  117 ++
 fs/dmapi/dmapi_dmattr.c    |  228 +++++
 fs/dmapi/dmapi_event.c     |  860 +++++++++++++++++++++
 fs/dmapi/dmapi_handle.c    |  119 ++
 fs/dmapi/dmapi_hole.c      |  119 ++
 fs/dmapi/dmapi_io.c        |  142 +++
 fs/dmapi/dmapi_kern.h      |  598 ++++++++++++++
 fs/dmapi/dmapi_mountinfo.c |  527 +++++++++++++
 fs/dmapi/dmapi_port.h      |  138 +++
 fs/dmapi/dmapi_private.h   |  619 +++++++++++++++
 fs/dmapi/dmapi_region.c    |   91 ++
 fs/dmapi/dmapi_register.c  | 1638 ++++++++++++++++++++++++++++++++++++++++
 fs/dmapi/dmapi_right.c     | 1256 ++++++++++++++++++++++++++++++
 fs/dmapi/dmapi_session.c   | 1824 +++++++++++++++++++++++++++++++++++++++++++++
 fs/dmapi/dmapi_sysent.c    |  801 +++++++++++++++++++
 fs/dmapi/sv.h              |   89 ++
 21 files changed, 10696 insertions(+)

--- /dev/null
+++ b/fs/dmapi/Makefile
@@ -0,0 +1,53 @@
+#
+# Copyright (c) 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# This program 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.
+#
+# This program is distributed in the hope that it would be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Further, this software is distributed without any warranty that it is
+# free of the rightful claim of any third person regarding infringement
+# or the like.	Any license provided herein, whether implied or
+# otherwise, applies only to this software file.  Patent licenses, if
+# any, provided herein do not apply to combinations of this program with
+# other software, or any other product whatsoever.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write the Free Software Foundation, Inc., 59
+# Temple Place - Suite 330, Boston MA 02111-1307, USA.
+#
+# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+# Mountain View, CA  94043, or:
+#
+# http://www.sgi.com
+#
+# For further information regarding this notice, see:
+#
+# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+#
+
+ifeq ($(CONFIG_DMAPI_DEBUG),y)
+	EXTRA_CFLAGS += -DDEBUG
+	EXTRA_CFLAGS += -g
+endif
+
+obj-$(CONFIG_DMAPI)		+= dmapi.o
+
+dmapi-y				+= dmapi_sysent.o \
+				   dmapi_attr.o \
+				   dmapi_config.o \
+				   dmapi_bulkattr.o \
+				   dmapi_dmattr.o \
+				   dmapi_event.o \
+				   dmapi_handle.o \
+				   dmapi_hole.o \
+				   dmapi_io.o \
+				   dmapi_mountinfo.o \
+				   dmapi_region.o \
+				   dmapi_register.o \
+				   dmapi_right.o \
+				   dmapi_session.o
--- /dev/null
+++ b/fs/dmapi/Status
@@ -0,0 +1,128 @@
+Jan21,04 - dm_get_bulkall is now implemented.  roehrich
+
+for linux:
+
+
+68 external interfaces in libdm
+
+   56 of those interfaces go through to dmi(), the kernel side of DMAPI
+
+
+
+Functions known to work
+----------------------------------------------
+
+dm_create_session
+dm_create_userevent
+dm_destroy_session
+dm_getall_sessions
+dm_getall_tokens
+dm_get_allocinfo
+dm_get_bulkall
+dm_get_bulkattr
+dm_get_config_events
+dm_get_dmattr
+dm_get_eventlist
+dm_get_events
+dm_get_fileattr
+dm_get_region
+dm_handle_free
+dm_init_attrloc
+dm_init_service
+dm_obj_ref_hold
+dm_obj_ref_query
+dm_obj_ref_rele
+dm_path_to_fshandle
+dm_path_to_handle
+dm_punch_hole
+dm_query_session
+dm_read_invis
+dm_remove_dmattr
+dm_respond_event
+dm_send_msg
+dm_set_disp
+dm_set_dmattr
+dm_set_eventlist
+dm_set_fileattr
+dm_set_region
+dm_sync_by_handle
+dm_write_invis
+35
+
+Functions that seem to work (would like more rigorous test case)
+------------------------------------------
+
+dm_pending
+dm_probe_hole		- one test case of test_hole.c fails
+dm_request_right
+3
+
+Functions untested but probably work
+----------------------------------------------
+
+dm_find_eventmsg
+dm_handle_cmp
+dm_handle_to_fshandle
+dm_handle_to_ino
+dm_release_right
+5
+
+Functions that do not work
+-----------------------------------------
+
+dm_get_dioinfo		- directio not implemented
+1
+
+Functions not supported in SGI DMAPI
+-------------------------------------------------------------
+
+dm_clear_inherit
+dm_create_by_handle
+dm_getall_inherit
+dm_mkdir_by_handle
+dm_set_inherit
+dm_symlink_by_handle
+
+
+
+
+Functions that seem to work (would like more rigorous test case)
+----------------------------------------------------------------
+
+dm_get_config
+dm_downgrade_right
+dm_get_mountinfo
+dm_set_return_on_destory
+dm_upgrade_right
+
+
+
+Functions that do not work
+-----------------------------------------------------------------
+
+dm_fd_to_handle		- Irix getf not implemented on linux
+dm_get_dirattrs		- null pointer reference
+dm_handle_to_path
+dm_getall_dmattr	- needs a copy_from_user in place of useracc
+
+
+Functions that are untested, but probably work
+-----------------------------------------------------------------
+
+dm_getall_disp
+dm_handle_hash
+dm_handle_is_valid
+dm_handle_to_fsid
+dm_handle_to_igen
+dm_make_fshandle
+dm_make_handle
+dm_move_event
+dm_query_right
+
+
+
+Other things not working
+----------------------------------
+
+- read/write events for memory-mapped I/O?
+
--- /dev/null
+++ b/fs/dmapi/dmapi.h
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (c) 1995-2003 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
+ * USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#ifndef __DMAPI_H__
+#define __DMAPI_H__
+
+#ifdef	__cplusplus
+extern	"C" {
+#endif
+
+#ifndef __KERNEL__
+#include <sys/types.h>
+#endif
+#include <linux/types.h>
+
+#ifndef __user
+#define __user
+#endif
+
+/**************************************************************************
+ *									  *
+ * The SGI implementation of DMAPI is based upon the X/Open document	  *
+ *	Systems Management: Data Storage Managment (XDSM) API		  *
+ * dated February 1997.	 Not all DMAPI functions and structure fields	  *
+ * have been implemented.  Most importantly, the DMAPI functions	  *
+ * dm_request_right, dm_release_right, dm_query_right, dm_upgrade_right	  *
+ * and dm_downgrade_right do not work as described in the specification.  *
+ *									  *
+ * The XFS filesystem currently does not allow its locking mechanisms to  *
+ * be externally accessed from user space.  While the above-mentioned	  *
+ * dm_xxx_right functions exist and can be called by applications, they	  *
+ * always return successfully without actually obtaining any locks	  *
+ * within the filesystem.						  *
+ *									  *
+ * Applications which do not need full rights support and which only	  *
+ * make dm_xxx_right calls in order to satisfy the input requirements of  *
+ * other DMAPI calls should be able to use these routines to avoid	  *
+ * having to implement special-case code for SGI platforms.  Applications *
+ * which truely need the capabilities of a full implementation of rights  *
+ * will unfortunately have to come up with alternate software solutions	  *
+ * until such time as rights can be completely implemented.		  *
+ *									  *
+ * Functions and structure fields defined within this file which are not  *
+ * supported in the SGI implementation of DMAPI are indicated by comments *
+ * following their definitions such as "not supported", or "not		  *
+ * completely supported".  Any function or field not so marked may be	  *
+ * assumed to work exactly according to the spec.			  *
+ *									  *
+ **************************************************************************/
+
+
+
+/* The first portion of this file contains defines and typedefs that are
+   DMAPI implementation-dependent, and could be different on other platforms.
+*/
+
+typedef __s64		dm_attrloc_t;
+typedef unsigned int	dm_boolean_t;
+typedef __u64		dm_eventset_t;
+typedef __u64		dm_fsid_t;
+typedef __u64		dm_ino_t;
+typedef __u32		dm_igen_t;
+typedef __s64		dm_off_t;
+typedef unsigned int	dm_sequence_t;
+typedef int		dm_sessid_t;
+typedef __u64		dm_size_t;
+typedef __s64		dm_ssize_t;
+typedef int		dm_token_t;
+
+/* XXX dev_t, mode_t, and nlink_t are not the same size in kernel space
+   and user space.  This affects the field offsets for dm_stat_t.
+   The following solution is temporary.
+
+   user space sizes:  dev_t=8  mode_t=4	 nlink_t=4
+   kernel space	   :  dev_t=2  mode_t=2	 nlink_t=2
+
+*/
+typedef __s64		dm_dev_t;
+typedef int		dm_mode_t;
+typedef int		dm_nlink_t;
+
+
+#define DM_REGION_NOEVENT	0x0
+#define DM_REGION_READ		0x1
+#define DM_REGION_WRITE		0x2
+#define DM_REGION_TRUNCATE	0x4
+
+/* Values for the mask argument used with dm_get_fileattr, dm_get_bulkattr,
+   dm_get_dirattrs, and dm_set_fileattr.
+*/
+
+#define DM_AT_MODE	0x0001
+#define DM_AT_UID	0x0002
+#define DM_AT_GID	0x0004
+#define DM_AT_ATIME	0x0008
+#define DM_AT_MTIME	0x0010
+#define DM_AT_CTIME	0x0020
+#define DM_AT_SIZE	0x0040
+#define DM_AT_DTIME	0x0080
+#define DM_AT_HANDLE	0x0100
+#define DM_AT_EMASK	0x0200
+#define DM_AT_PMANR	0x0400
+#define DM_AT_PATTR	0x0800
+#define DM_AT_STAT	0x1000
+#define DM_AT_CFLAG	0x2000
+
+#define DM_EV_WAIT	0x1		/* used in dm_get_events() */
+
+#define DM_MOUNT_RDONLY 0x1		/* me_mode field in dm_mount_event_t */
+
+#define DM_RR_WAIT	0x1
+
+#define DM_UNMOUNT_FORCE 0x1		/* ne_mode field in dm_namesp_event_t */
+
+#define DM_WRITE_SYNC	0x1		/* used in dm_write_invis() */
+
+#define DM_SESSION_INFO_LEN	256
+#define DM_NO_SESSION		0
+#define DM_TRUE			1
+#define DM_FALSE		0
+#define DM_INVALID_TOKEN	0
+#define DM_NO_TOKEN		(-1)
+#define DM_INVALID_HANP		NULL
+#define DM_INVALID_HLEN		0
+#define DM_GLOBAL_HANP		((void *)(1LL))
+#define DM_GLOBAL_HLEN		((size_t)(1))
+#define DM_VER_STR_CONTENTS	"SGI DMAPI (XDSM) API, Release 1.1."
+
+
+#define DMEV_SET(event_type, event_list) \
+	((event_list) |= (1 << (event_type)))
+#define DMEV_CLR(event_type, event_list) \
+	((event_list) &= ~(1 << (event_type)))
+#define DMEV_ISSET(event_type, event_list) \
+	(int)(((event_list) & (1 << (event_type))) != 0)
+#define DMEV_ZERO(event_list) \
+	(event_list) = 0
+
+
+typedef struct {
+	int	vd_offset;	/* offset from start of containing struct */
+	unsigned int	vd_length;	/* length of data starting at vd_offset */
+} dm_vardata_t;
+
+#define DM_GET_VALUE(p, field, type) \
+	((type) ((char *)(p) + (p)->field.vd_offset))
+
+#define DM_GET_LEN(p, field) \
+	((p)->field.vd_length)
+
+#define DM_STEP_TO_NEXT(p, type) \
+	((type) ((p)->_link ? (char *)(p) + (p)->_link : NULL))
+
+
+
+
+/* The remainder of this include file contains defines, typedefs, and
+   structures which are strictly defined by the DMAPI 2.3 specification.
+
+   (The _link field which appears in several structures is an
+   implementation-specific way to implement DM_STEP_TO_NEXT, and
+   should not be referenced directly by application code.)
+*/
+
+
+#define DM_ATTR_NAME_SIZE	8
+
+
+struct dm_attrname {
+	unsigned char	an_chars[DM_ATTR_NAME_SIZE];
+};
+typedef struct dm_attrname	dm_attrname_t;
+
+
+struct dm_attrlist {
+	int		_link;
+	dm_attrname_t	al_name;
+	dm_vardata_t	al_data;
+};
+typedef struct dm_attrlist	dm_attrlist_t;
+
+
+typedef enum {
+	DM_CONFIG_INVALID,
+	DM_CONFIG_BULKALL,
+	DM_CONFIG_CREATE_BY_HANDLE,
+	DM_CONFIG_DTIME_OVERLOAD,
+	DM_CONFIG_LEGACY,
+	DM_CONFIG_LOCK_UPGRADE,
+	DM_CONFIG_MAX_ATTR_ON_DESTROY,
+	DM_CONFIG_MAX_ATTRIBUTE_SIZE,
+	DM_CONFIG_MAX_HANDLE_SIZE,
+	DM_CONFIG_MAX_MANAGED_REGIONS,
+	DM_CONFIG_MAX_MESSAGE_DATA,
+	DM_CONFIG_OBJ_REF,
+	DM_CONFIG_PENDING,
+	DM_CONFIG_PERS_ATTRIBUTES,
+	DM_CONFIG_PERS_EVENTS,
+	DM_CONFIG_PERS_INHERIT_ATTRIBS,
+	DM_CONFIG_PERS_MANAGED_REGIONS,
+	DM_CONFIG_PUNCH_HOLE,
+	DM_CONFIG_TOTAL_ATTRIBUTE_SPACE,
+	DM_CONFIG_WILL_RETRY
+} dm_config_t;
+
+
+struct	dm_dioinfo {			/* non-standard SGI addition */
+	unsigned int	d_mem;
+	unsigned int	d_miniosz;
+	unsigned int	d_maxiosz;
+	dm_boolean_t	d_dio_only;
+};
+typedef struct dm_dioinfo	dm_dioinfo_t;
+
+
+struct dm_dispinfo {
+	int		_link;
+	unsigned int	di_pad1;		/* reserved; do not reference */
+	dm_vardata_t	di_fshandle;
+	dm_eventset_t	di_eventset;
+};
+typedef struct dm_dispinfo	dm_dispinfo_t;
+
+
+#ifndef HAVE_DM_EVENTTYPE_T
+#define HAVE_DM_EVENTTYPE_T
+typedef enum {
+	DM_EVENT_INVALID	= -1,
+	DM_EVENT_CANCEL		= 0,		/* not supported */
+	DM_EVENT_MOUNT		= 1,
+	DM_EVENT_PREUNMOUNT	= 2,
+	DM_EVENT_UNMOUNT	= 3,
+	DM_EVENT_DEBUT		= 4,		/* not supported */
+	DM_EVENT_CREATE		= 5,
+	DM_EVENT_CLOSE		= 6,		/* not supported */
+	DM_EVENT_POSTCREATE	= 7,
+	DM_EVENT_REMOVE		= 8,
+	DM_EVENT_POSTREMOVE	= 9,
+	DM_EVENT_RENAME		= 10,
+	DM_EVENT_POSTRENAME	= 11,
+	DM_EVENT_LINK		= 12,
+	DM_EVENT_POSTLINK	= 13,
+	DM_EVENT_SYMLINK	= 14,
+	DM_EVENT_POSTSYMLINK	= 15,
+	DM_EVENT_READ		= 16,
+	DM_EVENT_WRITE		= 17,
+	DM_EVENT_TRUNCATE	= 18,
+	DM_EVENT_ATTRIBUTE	= 19,
+	DM_EVENT_DESTROY	= 20,
+	DM_EVENT_NOSPACE	= 21,
+	DM_EVENT_USER		= 22,
+	DM_EVENT_MAX		= 23
+} dm_eventtype_t;
+#endif
+
+
+struct dm_eventmsg {
+	int		_link;
+	dm_eventtype_t	ev_type;
+	dm_token_t	ev_token;
+	dm_sequence_t	ev_sequence;
+	dm_vardata_t	ev_data;
+};
+typedef struct dm_eventmsg	dm_eventmsg_t;
+
+
+struct dm_cancel_event {			/* not supported */
+	dm_sequence_t	ce_sequence;
+	dm_token_t	ce_token;
+};
+typedef struct dm_cancel_event	dm_cancel_event_t;
+
+
+struct dm_data_event {
+	dm_vardata_t	de_handle;
+	dm_off_t	de_offset;
+	dm_size_t	de_length;
+};
+typedef struct dm_data_event dm_data_event_t;
+
+struct dm_destroy_event {
+	dm_vardata_t		ds_handle;
+	dm_attrname_t		ds_attrname;
+	dm_vardata_t		ds_attrcopy;
+};
+typedef struct dm_destroy_event dm_destroy_event_t;
+
+struct dm_mount_event {
+	dm_mode_t	me_mode;
+	dm_vardata_t	me_handle1;
+	dm_vardata_t	me_handle2;
+	dm_vardata_t	me_name1;
+	dm_vardata_t	me_name2;
+	dm_vardata_t	me_roothandle;
+};
+typedef struct dm_mount_event dm_mount_event_t;
+
+struct dm_namesp_event {
+	dm_mode_t	ne_mode;
+	dm_vardata_t	ne_handle1;
+	dm_vardata_t	ne_handle2;
+	dm_vardata_t	ne_name1;
+	dm_vardata_t	ne_name2;
+	int		ne_retcode;
+};
+typedef struct dm_namesp_event dm_namesp_event_t;
+
+
+typedef enum {
+	DM_EXTENT_INVALID,
+	DM_EXTENT_RES,
+	DM_EXTENT_HOLE
+} dm_extenttype_t;
+
+
+struct dm_extent {
+	dm_extenttype_t ex_type;
+	unsigned int	ex_pad1;		/* reserved; do not reference */
+	dm_off_t	ex_offset;
+	dm_size_t	ex_length;
+};
+typedef struct dm_extent dm_extent_t;
+
+struct dm_fileattr {
+	dm_mode_t	fa_mode;
+	uid_t		fa_uid;
+	gid_t		fa_gid;
+	time_t		fa_atime;
+	time_t		fa_mtime;
+	time_t		fa_ctime;
+	time_t		fa_dtime;
+	unsigned int	fa_pad1;		/* reserved; do not reference */
+	dm_off_t	fa_size;
+};
+typedef struct dm_fileattr dm_fileattr_t;
+
+
+struct dm_inherit {				/* not supported */
+	dm_attrname_t	ih_name;
+	dm_mode_t	ih_filetype;
+};
+typedef struct dm_inherit dm_inherit_t;
+
+
+typedef enum {
+	DM_MSGTYPE_INVALID,
+	DM_MSGTYPE_SYNC,
+	DM_MSGTYPE_ASYNC
+} dm_msgtype_t;
+
+
+struct dm_region {
+	dm_off_t	rg_offset;
+	dm_size_t	rg_size;
+	unsigned int	rg_flags;
+	unsigned int	rg_pad1;		/* reserved; do not reference */
+};
+typedef struct dm_region dm_region_t;
+
+
+typedef enum {
+	DM_RESP_INVALID,
+	DM_RESP_CONTINUE,
+	DM_RESP_ABORT,
+	DM_RESP_DONTCARE
+} dm_response_t;
+
+
+#ifndef HAVE_DM_RIGHT_T
+#define HAVE_DM_RIGHT_T
+typedef enum {
+	DM_RIGHT_NULL,
+	DM_RIGHT_SHARED,
+	DM_RIGHT_EXCL
+} dm_right_t;
+#endif
+
+
+struct dm_stat {
+	int		_link;
+	dm_vardata_t	dt_handle;
+	dm_vardata_t	dt_compname;
+	int		dt_nevents;
+	dm_eventset_t	dt_emask;
+	int		dt_pers;		/* field not supported */
+	int		dt_pmanreg;
+	time_t		dt_dtime;
+	unsigned int	dt_change;		/* field not supported */
+	unsigned int	dt_pad1;		/* reserved; do not reference */
+	dm_dev_t	dt_dev;
+	dm_ino_t	dt_ino;
+	dm_mode_t	dt_mode;
+	dm_nlink_t	dt_nlink;
+	uid_t		dt_uid;
+	gid_t		dt_gid;
+	dm_dev_t	dt_rdev;
+	unsigned int	dt_pad2;		/* reserved; do not reference */
+	dm_off_t	dt_size;
+	time_t		dt_atime;
+	time_t		dt_mtime;
+	time_t		dt_ctime;
+	unsigned int	dt_blksize;
+	dm_size_t	dt_blocks;
+
+	/* Non-standard filesystem-specific fields.  Currently XFS is the only
+	   supported filesystem type.
+	*/
+
+	__u64	dt_pad3;	/* reserved; do not reference */
+	int		dt_fstype;	/* filesystem index; see sysfs(2) */
+	union	{
+		struct	{
+			dm_igen_t	igen;
+			unsigned int	xflags;
+			unsigned int	extsize;
+			unsigned int	extents;
+			unsigned short	aextents;
+			unsigned short	dmstate;
+		} sgi_xfs;
+	} fsys_dep;
+};
+typedef struct dm_stat	dm_stat_t;
+
+#define dt_xfs_igen	fsys_dep.sgi_xfs.igen
+#define dt_xfs_xflags	fsys_dep.sgi_xfs.xflags
+#define dt_xfs_extsize	fsys_dep.sgi_xfs.extsize
+#define dt_xfs_extents	fsys_dep.sgi_xfs.extents
+#define dt_xfs_aextents fsys_dep.sgi_xfs.aextents
+#define dt_xfs_dmstate	fsys_dep.sgi_xfs.dmstate
+
+/* Flags for the non-standard dt_xfs_xflags field. */
+
+#define DM_XFLAG_REALTIME	0x00000001
+#define DM_XFLAG_PREALLOC	0x00000002
+#define DM_XFLAG_IMMUTABLE	0x00000008
+#define DM_XFLAG_APPEND		0x00000010
+#define DM_XFLAG_SYNC		0x00000020
+#define DM_XFLAG_NOATIME	0x00000040
+#define DM_XFLAG_NODUMP		0x00000080
+#define DM_XFLAG_HASATTR	0x80000000
+
+
+struct	dm_timestruct {
+	time_t		dm_tv_sec;
+	int		dm_tv_nsec;
+};
+typedef struct dm_timestruct dm_timestruct_t;
+
+
+struct	dm_xstat {				/* not supported */
+	dm_stat_t	dx_statinfo;
+	dm_vardata_t	dx_attrdata;
+};
+typedef struct dm_xstat dm_xstat_t;
+
+
+#define MAXDMFSFIDSZ	46
+
+struct dm_fid {
+	__u16	dm_fid_len;		/* length of remainder	*/
+	__u16	dm_fid_pad;
+	__u32	dm_fid_gen;		/* generation number	*/
+	__u64	dm_fid_ino;		/* 64 bits inode number */
+};
+typedef struct dm_fid dm_fid_t;
+
+
+struct dm_handle {
+	union {
+		__s64	    align;	/* force alignment of ha_fid	 */
+		dm_fsid_t  _ha_fsid;	/* unique file system identifier */
+	} ha_u;
+	dm_fid_t	ha_fid;		/* file system specific file ID	 */
+};
+typedef struct dm_handle dm_handle_t;
+#define ha_fsid ha_u._ha_fsid
+
+#define DM_HSIZE(handle)	(((char *) &(handle).ha_fid.dm_fid_pad	 \
+				 - (char *) &(handle))			  \
+				 + (handle).ha_fid.dm_fid_len)
+
+#define DM_HANDLE_CMP(h1, h2)	memcmp(h1, h2, sizeof(dm_handle_t))
+
+#define DM_FSHSIZE		sizeof(dm_fsid_t)
+
+
+/* The following list provides the prototypes for all functions defined in
+   the DMAPI interface.
+*/
+
+extern int
+dm_clear_inherit(				/* not supported */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep);
+
+extern int
+dm_create_by_handle(				/* not supported */
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname);
+
+extern int
+dm_create_session(
+	dm_sessid_t	oldsid,
+	char		__user *sessinfop,
+	dm_sessid_t	__user *newsidp);
+
+extern int
+dm_create_userevent(
+	dm_sessid_t	sid,
+	size_t		msglen,
+	void		__user *msgdatap,
+	dm_token_t	__user *tokenp);
+
+extern int
+dm_destroy_session(
+	dm_sessid_t	sid);
+
+extern int
+dm_downgrade_right(		/* not completely supported; see caveat above */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token);
+
+extern int
+dm_fd_to_handle(
+	int		fd,
+	void		**hanpp,
+	size_t		*hlenp);
+
+extern int
+dm_find_eventmsg(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_get_allocinfo(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	*offp,
+	unsigned int	nelem,
+	dm_extent_t	*extentp,
+	unsigned int	*nelemp);
+
+extern int
+dm_get_bulkall(					/* not supported */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	mask,
+	dm_attrname_t	*attrnamep,
+	dm_attrloc_t	*locp,
+	size_t		buflen,
+	void		*bufp,
+	size_t		*rlenp);
+
+extern int
+dm_get_bulkattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	mask,
+	dm_attrloc_t	*locp,
+	size_t		buflen,
+	void		*bufp,
+	size_t		*rlenp);
+
+extern int
+dm_get_config(
+	void		__user *hanp,
+	size_t		hlen,
+	dm_config_t	flagname,
+	dm_size_t	__user *retvalp);
+
+extern int
+dm_get_config_events(
+	void		__user *hanp,
+	size_t		hlen,
+	unsigned int	nelem,
+	dm_eventset_t	__user *eventsetp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_get_dirattrs(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	mask,
+	dm_attrloc_t	*locp,
+	size_t		buflen,
+	void		*bufp,
+	size_t		*rlenp);
+
+extern int
+dm_get_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_get_eventlist(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	nelem,
+	dm_eventset_t	__user *eventsetp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_get_events(
+	dm_sessid_t	sid,
+	unsigned int	maxmsgs,
+	unsigned int	flags,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_get_fileattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	mask,
+	dm_stat_t	__user *statp);
+
+extern int
+dm_get_mountinfo(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_get_region(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	nelem,
+	dm_region_t	__user *regbufp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_getall_disp(
+	dm_sessid_t	sid,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_getall_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern int
+dm_getall_inherit(				/* not supported */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	nelem,
+	dm_inherit_t	__user *inheritbufp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_getall_sessions(
+	unsigned int	nelem,
+	dm_sessid_t	__user *sidbufp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_getall_tokens(
+	dm_sessid_t	sid,
+	unsigned int	nelem,
+	dm_token_t	__user *tokenbufp,
+	unsigned int	__user *nelemp);
+
+extern int
+dm_handle_cmp(
+	void		*hanp1,
+	size_t		hlen1,
+	void		*hanp2,
+	size_t		hlen2);
+
+extern void
+dm_handle_free(
+	void		*hanp,
+	size_t		hlen);
+
+extern u_int
+dm_handle_hash(
+	void		*hanp,
+	size_t		hlen);
+
+extern dm_boolean_t
+dm_handle_is_valid(
+	void		*hanp,
+	size_t		hlen);
+
+extern int
+dm_handle_to_fshandle(
+	void		*hanp,
+	size_t		hlen,
+	void		**fshanpp,
+	size_t		*fshlenp);
+
+extern int
+dm_handle_to_fsid(
+	void		*hanp,
+	size_t		hlen,
+	dm_fsid_t	*fsidp);
+
+extern int
+dm_handle_to_igen(
+	void		*hanp,
+	size_t		hlen,
+	dm_igen_t	*igenp);
+
+extern int
+dm_handle_to_ino(
+	void		*hanp,
+	size_t		hlen,
+	dm_ino_t	*inop);
+
+extern int
+dm_handle_to_path(
+	void		*dirhanp,
+	size_t		dirhlen,
+	void		*targhanp,
+	size_t		targhlen,
+	size_t		buflen,
+	char		*pathbufp,
+	size_t		*rlenp);
+
+extern int
+dm_init_attrloc(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrloc_t	__user *locp);
+
+extern int
+dm_init_service(
+	char		**versionstrpp);
+
+extern int
+dm_make_handle(
+	dm_fsid_t	*fsidp,
+	dm_ino_t	*inop,
+	dm_igen_t	*igenp,
+	void		**hanpp,
+	size_t		*hlenp);
+
+extern int
+dm_make_fshandle(
+	dm_fsid_t	*fsidp,
+	void		**hanpp,
+	size_t		*hlenp);
+
+extern int
+dm_mkdir_by_handle(				/* not supported */
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname);
+
+extern int
+dm_move_event(
+	dm_sessid_t	srcsid,
+	dm_token_t	token,
+	dm_sessid_t	targetsid,
+	dm_token_t	__user *rtokenp);
+
+extern int
+dm_obj_ref_hold(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen);
+
+extern int
+dm_obj_ref_query(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		*hanp,
+	size_t		hlen);
+
+extern int
+dm_obj_ref_rele(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen);
+
+extern int
+dm_path_to_fshandle(
+	char		*path,
+	void		**hanpp,
+	size_t		*hlenp);
+
+extern int
+dm_path_to_handle(
+	char		*path,
+	void		**hanpp,
+	size_t		*hlenp);
+
+extern int
+dm_pending(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	dm_timestruct_t	__user *delay);
+
+extern int
+dm_probe_hole(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len,
+	dm_off_t	__user	*roffp,
+	dm_size_t	__user *rlenp);
+
+extern int
+dm_punch_hole(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len);
+
+extern int
+dm_query_right(			/* not completely supported; see caveat above */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_right_t	__user *rightp);
+
+extern int
+dm_query_session(
+	dm_sessid_t	sid,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp);
+
+extern dm_ssize_t
+dm_read_invis(
+	dm_sessid_t	sid,
+	void		*hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		*bufp);
+
+extern int
+dm_release_right(		/* not completely supported; see caveat above */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token);
+
+extern int
+dm_remove_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	int		setdtime,
+	dm_attrname_t	__user *attrnamep);
+
+extern int
+dm_request_right(		/* not completely supported; see caveat above */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	flags,
+	dm_right_t	right);
+
+extern int
+dm_respond_event(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	dm_response_t	response,
+	int		reterror,
+	size_t		buflen,
+	void		__user *respbufp);
+
+extern int
+dm_send_msg(
+	dm_sessid_t	targetsid,
+	dm_msgtype_t	msgtype,
+	size_t		buflen,
+	void		__user *bufp);
+
+extern int
+dm_set_disp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_eventset_t	__user *eventsetp,
+	unsigned int	maxevent);
+
+extern int
+dm_set_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	int		setdtime,
+	size_t		buflen,
+	void		__user *bufp);
+
+extern int
+dm_set_eventlist(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_eventset_t	__user *eventsetp,
+	unsigned int	maxevent);
+
+extern int
+dm_set_fileattr(
+	dm_sessid_t	sid,
+	void 		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	mask,
+	dm_fileattr_t	__user *attrp);
+
+extern int
+dm_set_inherit(					/* not supported */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	mode_t		mode);
+
+extern int
+dm_set_region(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	unsigned int	nelem,
+	dm_region_t	__user *regbufp,
+	dm_boolean_t	__user *exactflagp);
+
+extern int
+dm_set_return_on_destroy(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	dm_boolean_t	enable);
+
+extern int
+dm_symlink_by_handle(				/* not supported */
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname,
+	char		__user *path);
+
+extern int
+dm_sync_by_handle(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token);
+
+extern int
+dm_upgrade_right(		/* not completely supported; see caveat above */
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token);
+
+extern dm_ssize_t
+dm_write_invis(
+	dm_sessid_t	sid,
+	void		*hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	int		flags,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		*bufp);
+
+/* Non-standard SGI additions to the DMAPI interface. */
+
+int
+dm_open_by_handle(
+	void		__user *hanp,
+	size_t		hlen,
+	int		mode);
+
+extern int
+dm_get_dioinfo(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_dioinfo_t	__user *diop);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* __DMAPI_H__ */
--- /dev/null
+++ b/fs/dmapi/dmapi_attr.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+/* Retrieve attributes for a single file, directory or symlink. */
+
+int
+dm_get_fileattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		mask,
+	dm_stat_t	__user *statp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_fileattr(tdp->td_ip, tdp->td_right,
+		mask, statp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+/* Set one or more file attributes of a file, directory, or symlink. */
+
+int
+dm_set_fileattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		mask,
+	dm_fileattr_t	__user *attrp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->set_fileattr(tdp->td_ip, tdp->td_right,
+		mask, attrp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_bulkattr.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_init_attrloc(
+	dm_sessid_t	sid,
+	void		__user  *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrloc_t	__user *locp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS|DM_TDT_DIR,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->init_attrloc(tdp->td_ip, tdp->td_right, locp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+/*
+ * Retrieves both standard and DM specific file attributes for the file
+ * system indicated by the handle. (The FS has to be mounted).
+ * Syscall returns 1 to indicate SUCCESS and more information is available.
+ * -1 is returned on error, and errno will be set appropriately.
+ * 0 is returned upon successful completion.
+ */
+
+int
+dm_get_bulkattr_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		mask,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_bulkattr_rvp(tdp->td_ip, tdp->td_right,
+			mask, locp, buflen, bufp, rlenp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+/*
+ * Retrieves attributes of directory entries given a handle to that
+ * directory. Iterative.
+ * Syscall returns 1 to indicate SUCCESS and more information is available.
+ * -1 is returned on error, and errno will be set appropriately.
+ * 0 is returned upon successful completion.
+ */
+
+int
+dm_get_dirattrs_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		mask,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_DIR,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_dirattrs_rvp(tdp->td_ip, tdp->td_right,
+		mask, locp, buflen, bufp, rlenp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_get_bulkall_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		mask,
+	dm_attrname_t	__user *attrnamep,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_bulkall_rvp(tdp->td_ip, tdp->td_right,
+		mask, attrnamep, locp, buflen, bufp, rlenp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_config.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <asm/uaccess.h>
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+int
+dm_get_config(
+	void		__user *hanp,
+	size_t		hlen,
+	dm_config_t	flagname,
+	dm_size_t	__user *retvalp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	dm_size_t	retval;
+	int		system = 1;
+	int		error;
+
+	/* Trap and process configuration parameters which are system-wide. */
+
+	switch (flagname) {
+	case DM_CONFIG_LEGACY:
+	case DM_CONFIG_PENDING:
+	case DM_CONFIG_OBJ_REF:
+		retval = DM_TRUE;
+		break;
+	case DM_CONFIG_MAX_MESSAGE_DATA:
+		retval = DM_MAX_MSG_DATA;
+		break;
+	default:
+		system = 0;
+		break;
+	}
+	if (system) {
+		if (copy_to_user(retvalp, &retval, sizeof(retval)))
+			return(-EFAULT);
+		return(0);
+	}
+
+	/* Must be filesystem-specific.	 Convert the handle into an inode. */
+
+	if ((error = dm_get_config_tdp(hanp, hlen, &tdp)) != 0)
+		return(error);
+
+	/* Now call the filesystem-specific routine to determine the
+	   value of the configuration option for that filesystem.
+	*/
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_config(tdp->td_ip, tdp->td_right,
+		flagname, retvalp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_get_config_events(
+	void		__user *hanp,
+	size_t		hlen,
+	u_int		nelem,
+	dm_eventset_t	__user *eventsetp,
+	u_int		__user *nelemp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	/* Convert the handle into an inode. */
+
+	if ((error = dm_get_config_tdp(hanp, hlen, &tdp)) != 0)
+		return(error);
+
+	/* Now call the filesystem-specific routine to determine the
+	   events supported by that filesystem.
+	*/
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_config_events(tdp->td_ip, tdp->td_right,
+		nelem, eventsetp, nelemp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_dmattr.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_clear_inherit(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->clear_inherit(tdp->td_ip, tdp->td_right,
+		attrnamep);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_get_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_dmattr(tdp->td_ip, tdp->td_right,
+		attrnamep, buflen, bufp, rlenp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_getall_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->getall_dmattr(tdp->td_ip, tdp->td_right,
+		buflen, bufp, rlenp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_getall_inherit(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		nelem,
+	dm_inherit_t	__user *inheritbufp,
+	u_int		__user *nelemp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->getall_inherit(tdp->td_ip, tdp->td_right,
+		nelem, inheritbufp, nelemp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_remove_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	int		setdtime,
+	dm_attrname_t	__user *attrnamep)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->remove_dmattr(tdp->td_ip, tdp->td_right,
+		setdtime, attrnamep);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_set_dmattr(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	int		setdtime,
+	size_t		buflen,
+	void		__user *bufp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->set_dmattr(tdp->td_ip, tdp->td_right,
+		attrnamep, setdtime, buflen, bufp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_set_inherit(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	mode_t		mode)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->set_inherit(tdp->td_ip, tdp->td_right,
+		attrnamep, mode);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_event.c
@@ -0,0 +1,860 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include <asm/uaccess.h>
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+/* The "rights" portion of the DMAPI spec is not currently implemented.	 A
+   framework for rights is provided in the code, but turns out to be a noop
+   in practice.	 The following comments are a brain dump to serve as input to
+   the poor soul that eventually has to get DMAPI rights working in IRIX.
+
+   A DMAPI right is similar but not identical to the mrlock_t mechanism
+   already used within the kernel.  The similarities are that it is a
+   sleeping lock, and that a multiple-reader, single-writer protocol is used.
+   How locks are obtained and dropped are different however.  With a mrlock_t,
+   a thread grabs the lock, does some stuff, then drops the lock, and all other
+   threads block in the meantime (assuming a write lock).  There is a one-to-
+   one relationship between the lock and the thread which obtained the lock.
+   Not so with DMAPI right locks.  A DMAPI lock is associated with a particular
+   session/token/hanp/hlen quad; since there is a dm_tokdata_t structure for
+   each such quad, you can think of it as a one-to-one relationship between the
+   lock and a dm_tokdata_t.  Any application thread which presents the correct
+   quad is entitled to grab or release the lock, or to use the rights
+   associated with that lock.  The thread that grabs the lock does not have to
+   be the one to use the lock, nor does it have to be the thread which drops
+   the lock.  The lock can be held for very long periods of time, even across
+   multiple systems calls by multiple application threads.  The idea is that a
+   coordinated group of DMAPI application threads can grab the lock, issue a
+   series of inode accesses and/or updates, then drop the lock, and be assured
+   that no other thread in the system could be modifying the inode at the same
+   time.  The kernel is expected to blindly trust that the application will
+   not forget to unlock inodes it has locked, and will not deadlock itself
+   against the kernel.
+
+   There are two types of DMAPI rights, file object (inode) and filesystem
+   object (superblock?).  An inode right is the equivalent of the combination
+   of both the XFS ilock and iolock; if held exclusively, no data or metadata
+   within the file can be changed by non-lock-holding threads.	The filesystem
+   object lock is a little fuzzier; I think that if it is held, things like
+   unmounts can be blocked, plus there is an event mask associated with the
+   filesystem which can't be updated without the lock.	(By the way, that
+   event mask is supposed to be persistent in the superblock; add that to
+   your worklist :-)
+
+   All events generated by XFS currently arrive with no rights, i.e.
+   DM_RIGHT_NULL, and return to the filesystem with no rights.	It would be
+   smart to leave it this way if possible, because it otherwise becomes more
+   likely that an application thread will deadlock against the kernel if the
+   one responsible for calling dm_get_events() happens to touch a file which
+   was locked at the time the event was queued.	 Since the thread is blocked,
+   it can't read the event in order to find and drop the lock.	Catch-22.  If
+   you do have events that arrive with non-null rights, then dm_enqueue() needs
+   to have code added for synchronous events which atomically switches the
+   right from being a thread-based right to a dm_tokdata_t-based right without
+   allowing the lock to drop in between.  You will probably have to add a new
+   dm_fsys_vector entry point to do this.  The lock can't be lost during the
+   switch, or other threads might change the inode or superblock in between.
+   Likewise, if you need to return to the filesystem holding a right, then
+   you need a DMAPI-to-thread atomic switch to occur, most likely in
+   dm_change_right().  Again, the lock must not be lost during the switch; the
+   DMAPI spec spends a couple of pages stressing this.	Another dm_fsys_vector
+   entry point is probably the answer.
+
+   There are several assumptions implied in the current layout of the code.
+   First of all, if an event returns to the filesystem with a return value of
+   zero, then the filesystem can assume that any locks (rights) held at the
+   start of the event are still in effect at the end of the event.  (Note that
+   the application could have temporarily dropped and reaquired the right
+   while the event was outstanding, however).  If the event returns to the
+   filesystem with an errno, then the filesystem must assume that it has lost
+   any and all rights associated with any of the objects in the event.	This
+   was done for a couple of reasons.  First of all, since an errno is being
+   returned, most likely the filesystem is going to immediately drop all the
+   locks anyway.  If the DMAPI code was required to unconditionally reobtain
+   all locks before returning to the filesystem, then dm_pending() wouldn't
+   work for NFS server threads because the process would block indefinitely
+   trying to get its thread-based rights back, because the DMAPI-rights
+   associated with the dm_tokdata_t in the outstanding event would prevent
+   the rights from being obtained.  That would be a bad thing.	We wouldn't
+   be able to let users Cntl-C out of read/write/truncate events either.
+
+   If a case should ever surface where the thread has lost its rights even
+   though it has a zero return status, or where the thread has rights even
+   though it is returning with an errno, then this logic will have to be
+   reworked.  This could be done by changing the 'right' parameters on all
+   the event calls to (dm_right_t *), so that they could serve both as IN
+   and OUT parameters.
+
+   Some events such as DM_EVENT_DESTROY arrive without holding an inode
+   reference; if you don't have an inode reference, you can't have a right
+   on the file.
+
+   One more quirk.  The DM_EVENT_UNMOUNT event is defined to be synchronous
+   when it's behavior is asynchronous.	If an unmount event arrives with
+   rights, the event should return with the same rights and should NOT leave
+   any rights in the dm_tokdata_t where the application could use them.
+*/
+
+
+#define GETNEXTOFF(vdat)	((vdat).vd_offset + (vdat).vd_length)
+#define HANDLE_SIZE(tdp)	\
+	((tdp)->td_type & DM_TDT_VFS ? DM_FSHSIZE : DM_HSIZE((tdp)->td_handle))
+
+
+/* Given an inode pointer in a filesystem known to support DMAPI,
+   build a tdp structure for the corresponding inode.
+*/
+
+static dm_tokdata_t *
+dm_ip_data(
+	struct inode	*ip,
+	dm_right_t	right,
+	int		referenced)	/* != 0, caller holds inode reference */
+{
+	int		error;
+	dm_tokdata_t	*tdp;
+	int		filetype;
+
+	tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL);
+	if (tdp == NULL) {
+		printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+		return NULL;
+	}
+
+	tdp->td_next = NULL;
+	tdp->td_tevp = NULL;
+	tdp->td_app_ref = 0;
+	tdp->td_orig_right = right;
+	tdp->td_right = right;
+	tdp->td_flags = DM_TDF_ORIG;
+	if (referenced) {
+		tdp->td_flags |= DM_TDF_EVTREF;
+	}
+
+	filetype = ip->i_mode & S_IFMT;
+	if (filetype == S_IFREG) {
+		tdp->td_type = DM_TDT_REG;
+	} else if (filetype == S_IFDIR) {
+		tdp->td_type = DM_TDT_DIR;
+	} else if (filetype == S_IFLNK) {
+		tdp->td_type = DM_TDT_LNK;
+	} else {
+		tdp->td_type = DM_TDT_OTH;
+	}
+
+	if (referenced) {
+		tdp->td_ip = ip;
+	} else {
+		tdp->td_ip = NULL;
+	}
+	tdp->td_vcount = 0;
+
+	if ((error = dm_ip_to_handle(ip, &tdp->td_handle)) != 0) {
+		panic("dm_ip_data: dm_ip_to_handle failed for ip %p in "
+			"a DMAPI filesystem, errno %d\n", ip, error);
+	}
+
+	return(tdp);
+}
+
+
+/* Given a sb pointer to a filesystem known to support DMAPI, build a tdp
+   structure for that sb.
+*/
+static dm_tokdata_t *
+dm_sb_data(
+	struct super_block *sb,
+	struct inode	*ip,		/* will be NULL for DM_EVENT_UNMOUNT */
+	dm_right_t	right)
+{
+	dm_tokdata_t	*tdp;
+	struct filesystem_dmapi_operations *dops;
+	dm_fsid_t	fsid;
+
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+	dops->get_fsid(sb, &fsid);
+
+	tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL);
+	if (tdp == NULL) {
+		printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+		return NULL;
+	}
+
+	tdp->td_next = NULL;
+	tdp->td_tevp = NULL;
+	tdp->td_app_ref = 0;
+	tdp->td_orig_right = right;
+	tdp->td_right = right;
+	tdp->td_flags = DM_TDF_ORIG;
+	if (ip) {
+		tdp->td_flags |= DM_TDF_EVTREF;
+	}
+	tdp->td_type = DM_TDT_VFS;
+	tdp->td_ip = ip;
+	tdp->td_vcount = 0;
+
+	memcpy(&tdp->td_handle.ha_fsid, &fsid, sizeof(fsid));
+	memset((char *)&tdp->td_handle.ha_fsid + sizeof(fsid), 0,
+		sizeof(tdp->td_handle) - sizeof(fsid));
+
+	return(tdp);
+}
+
+
+/* Link a tdp structure into the tevp. */
+
+static void
+dm_add_handle_to_event(
+	dm_tokevent_t	*tevp,
+	dm_tokdata_t	*tdp)
+{
+	tdp->td_next = tevp->te_tdp;
+	tevp->te_tdp = tdp;
+	tdp->td_tevp = tevp;
+}
+
+
+/* Generate the given data event for the inode, and wait for a reply.  The
+   caller must guarantee that the inode's reference count is greater than zero
+   so that the filesystem can't disappear while the request is outstanding.
+*/
+
+int
+dm_send_data_event(
+	dm_eventtype_t	event,
+	struct inode	*ip,
+	dm_right_t	vp_right,	/* current right for ip */
+	dm_off_t	offset,
+	size_t		length,
+	int		flags)		/* 0 or DM_FLAGS_NDELAY */
+{
+	dm_data_event_t *datap;
+	dm_tokevent_t	*tevp;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	tdp = dm_ip_data(ip, vp_right, /* reference held */ 1);
+	if (tdp == NULL)
+		return -ENOMEM;
+
+	/* Calculate the size of the event in bytes, create an event structure
+	   for it, and insert the file's handle into the event.
+	*/
+
+	tevp = dm_evt_create_tevp(event, HANDLE_SIZE(tdp), (void **)&datap);
+	if (tevp == NULL) {
+		kmem_cache_free(dm_tokdata_cachep, tdp);
+		return(-ENOMEM);
+	}
+	dm_add_handle_to_event(tevp, tdp);
+
+	/* Now fill in all the dm_data_event_t fields. */
+
+	datap->de_handle.vd_offset = sizeof(*datap);
+	datap->de_handle.vd_length = HANDLE_SIZE(tdp);
+	memcpy((char *)datap + datap->de_handle.vd_offset, &tdp->td_handle,
+		datap->de_handle.vd_length);
+	datap->de_offset = offset;
+	datap->de_length = length;
+
+	/* Queue the message and wait for the reply. */
+
+	error = dm_enqueue_normal_event(ip->i_sb, &tevp, flags);
+
+	/* If no errors occurred, we must leave with the same rights we had
+	   upon entry.	If errors occurred, we must leave with no rights.
+	*/
+
+	dm_evt_rele_tevp(tevp, error);
+
+	return(error);
+}
+
+
+/* Generate the destroy event for the inode and wait until the request has been
+   queued.  The caller does not hold an inode reference or a right on the inode,
+   but it must otherwise lock down the inode such that the filesystem can't
+   disappear while the request is waiting to be queued.	 While waiting to be
+   queued, the inode must not be referenceable either by path or by a call
+   to dm_handle_to_ip().
+*/
+
+int
+dm_send_destroy_event(
+	struct inode	*ip,
+	dm_right_t	vp_right)	/* always DM_RIGHT_NULL */
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokevent_t	*tevp;
+	dm_tokdata_t	*tdp;
+	dm_destroy_event_t *destp;
+	dm_attrname_t	attrname;
+	char		*value;
+	int		value_len;
+	int		error;
+
+	tdp = dm_ip_data(ip, vp_right, /* no reference held */ 0);
+	if (tdp == NULL)
+		return -ENOMEM;
+
+	if ((error = dm_waitfor_destroy_attrname(ip->i_sb, &attrname)) != 0)
+		return(error);
+
+	/* If a return-on-destroy attribute name exists for this filesystem,
+	   see if the object being deleted has this attribute.	If the object
+	   doesn't have the attribute or if we encounter an error, then send
+	   the event without the attribute.
+	 */
+
+	value_len = -1;		/* because zero is a valid attribute length */
+	if (attrname.an_chars[0] != '\0') {
+		fsys_vector = dm_fsys_vector(ip);
+		error = fsys_vector->get_destroy_dmattr(ip, vp_right, &attrname,
+			&value, &value_len);
+		if (error && error != -ENODATA)
+			return error;
+	}
+
+	/* Now that we know the size of the attribute value, if any, calculate
+	   the size of the event in bytes, create an event structure for it,
+	   and insert the handle into the event.
+	*/
+
+	tevp = dm_evt_create_tevp(DM_EVENT_DESTROY,
+		HANDLE_SIZE(tdp) + (value_len >= 0 ? value_len : 0),
+		(void **)&destp);
+	if (tevp == NULL) {
+		kmem_cache_free(dm_tokdata_cachep, tdp);
+		if (value_len > 0)
+			kfree(value);
+		return(-ENOMEM);
+	}
+	dm_add_handle_to_event(tevp, tdp);
+
+	/* Now fill in all the dm_destroy_event_t fields. */
+
+	destp->ds_handle.vd_offset = sizeof(*destp);
+	destp->ds_handle.vd_length = HANDLE_SIZE(tdp);
+	memcpy((char *)destp + destp->ds_handle.vd_offset, &tdp->td_handle,
+		destp->ds_handle.vd_length);
+	if (value_len >= 0) {
+		destp->ds_attrname = attrname;
+		destp->ds_attrcopy.vd_length = value_len;
+		if (value_len == 0) {
+			destp->ds_attrcopy.vd_offset = 0;
+		} else {
+			destp->ds_attrcopy.vd_offset = GETNEXTOFF(destp->ds_handle);
+			memcpy((char *)destp + destp->ds_attrcopy.vd_offset, value,
+				value_len);
+			kfree(value);
+		}
+	}
+
+	/* Queue the message asynchronously. */
+
+	error = dm_enqueue_normal_event(ip->i_sb, &tevp, 0);
+
+	/* Since we had no rights upon entry, we have none to reobtain before
+	   leaving.
+	*/
+
+	dm_evt_rele_tevp(tevp, 1);
+
+	return(error);
+}
+
+
+/* The dm_mount_event_t event is sent in turn to all sessions that have asked
+   for it until one either rejects it or accepts it.  The filesystem is not
+   going anywhere because the mount is blocked until the event is answered.
+*/
+
+int
+dm_send_mount_event(
+	struct super_block *sb,		/* filesystem being mounted */
+	dm_right_t	vfsp_right,
+	struct inode	*ip,		/* mounted on directory */
+	dm_right_t	vp_right,
+	struct inode	*rootip,
+	dm_right_t	rootvp_right,
+	char		*name1,		/* mount path */
+	char		*name2)		/* filesystem device name */
+{
+	int		error;
+	dm_tokevent_t	*tevp = NULL;
+	dm_tokdata_t	*tdp1 = NULL;	/* filesystem handle for event */
+	dm_tokdata_t	*tdp2 = NULL;	/* file handle for mounted-on dir. */
+	dm_tokdata_t	*tdp3 = NULL;	/* file handle for root inode */
+	dm_mount_event_t *mp;
+	size_t		nextoff;
+
+	/* Convert the sb to a filesystem handle, and ip and rootip into
+	   file handles.  ip (the mounted-on directory) may not have a handle
+	   if it is a different filesystem type which does not support DMAPI.
+	*/
+
+	tdp1 = dm_sb_data(sb, rootip, vfsp_right);
+	if (tdp1 == NULL)
+		goto out_nomem;
+
+	if ((ip == NULL) || dm_check_dmapi_ip(ip)) {
+		ip = NULL;	/* we are mounting on non-DMAPI FS */
+	} else {
+		tdp2 = dm_ip_data(ip, vp_right, /* reference held */ 1);
+		if (tdp2 == NULL)
+			goto out_nomem;
+	}
+
+	tdp3 = dm_ip_data(rootip, rootvp_right, /* reference held */ 1);
+	if (tdp3 == NULL)
+		goto out_nomem;
+
+	/* Calculate the size of the event in bytes, create an event structure
+	   for it, and insert the handles into the event.
+	*/
+
+	tevp = dm_evt_create_tevp(DM_EVENT_MOUNT,
+			HANDLE_SIZE(tdp1) + (ip ? HANDLE_SIZE(tdp2) : 0) +
+			HANDLE_SIZE(tdp3) + strlen(name1) + 1 +
+			strlen(name2) + 1, (void **)&mp);
+	if (tevp == NULL)
+		goto out_nomem;
+
+	dm_add_handle_to_event(tevp, tdp1);
+	if (ip)
+		dm_add_handle_to_event(tevp, tdp2);
+	dm_add_handle_to_event(tevp, tdp3);
+
+	/* Now fill in all the dm_mount_event_t fields. */
+
+	mp->me_handle1.vd_offset = sizeof(*mp);
+	mp->me_handle1.vd_length = HANDLE_SIZE(tdp1);
+	memcpy((char *) mp + mp->me_handle1.vd_offset, &tdp1->td_handle,
+			mp->me_handle1.vd_length);
+	nextoff = GETNEXTOFF(mp->me_handle1);
+
+	if (ip) {
+		mp->me_handle2.vd_offset = nextoff;
+		mp->me_handle2.vd_length = HANDLE_SIZE(tdp2);
+		memcpy((char *)mp + mp->me_handle2.vd_offset, &tdp2->td_handle,
+			mp->me_handle2.vd_length);
+		nextoff = GETNEXTOFF(mp->me_handle2);
+	}
+
+	mp->me_name1.vd_offset = nextoff;
+	mp->me_name1.vd_length = strlen(name1) + 1;
+	memcpy((char *)mp + mp->me_name1.vd_offset, name1, mp->me_name1.vd_length);
+	nextoff = GETNEXTOFF(mp->me_name1);
+
+	mp->me_name2.vd_offset = nextoff;
+	mp->me_name2.vd_length = strlen(name2) + 1;
+	memcpy((char *)mp + mp->me_name2.vd_offset, name2, mp->me_name2.vd_length);
+	nextoff = GETNEXTOFF(mp->me_name2);
+
+	mp->me_roothandle.vd_offset = nextoff;
+	mp->me_roothandle.vd_length = HANDLE_SIZE(tdp3);
+	memcpy((char *)mp + mp->me_roothandle.vd_offset, &tdp3->td_handle,
+			mp->me_roothandle.vd_length);
+
+	mp->me_mode = (sb->s_flags & MS_RDONLY ? DM_MOUNT_RDONLY : 0);
+
+	/* Queue the message and wait for the reply. */
+
+	error = dm_enqueue_mount_event(sb, tevp);
+
+	/* If no errors occurred, we must leave with the same rights we had
+	   upon entry.	If errors occurred, we must leave with no rights.
+	*/
+
+	dm_evt_rele_tevp(tevp, error);
+
+	return(error);
+
+out_nomem:
+	if (tevp)
+		kfree(tevp);
+	if (tdp1)
+		kmem_cache_free(dm_tokdata_cachep, tdp1);
+	if (tdp2)
+		kmem_cache_free(dm_tokdata_cachep, tdp2);
+	if (tdp3)
+		kmem_cache_free(dm_tokdata_cachep, tdp3);
+	return -ENOMEM;
+}
+
+
+/* Generate an DM_EVENT_UNMOUNT event and wait for a reply.  The 'retcode'
+   field indicates whether this is a successful or unsuccessful unmount.
+   If successful, the filesystem is already unmounted, and any pending handle
+   reference to the filesystem will be failed.	If the unmount was
+   unsuccessful, then the filesystem will be placed back into full service.
+
+   The DM_EVENT_UNMOUNT event should really be asynchronous, because the
+   application has no control over whether or not the unmount succeeds.	 (The
+   DMAPI spec defined it that way because asynchronous events aren't always
+   guaranteed to be delivered.)
+
+   Since the filesystem is already unmounted in the successful case, the
+   DM_EVENT_UNMOUNT event can't make available any inode to be used in
+   subsequent sid/hanp/hlen/token calls by the application.  The event will
+   hang around until the application does a DM_RESP_CONTINUE, but the handle
+   within the event is unusable by the application.
+*/
+
+void
+dm_send_unmount_event(
+	struct super_block *sb,
+	struct inode	*ip,		/* NULL if unmount successful */
+	dm_right_t	vfsp_right,
+	mode_t		mode,
+	int		retcode,	/* errno, if unmount failed */
+	int		flags)
+{
+	dm_namesp_event_t	*np;
+	dm_tokevent_t	*tevp;
+	dm_tokdata_t	*tdp1;
+
+	/* If the unmount failed, put the filesystem back into full service,
+	   allowing blocked handle references to finish.  If it succeeded, put
+	   the filesystem into the DM_STATE_UNMOUNTED state and fail all
+	   blocked DM_NO_TOKEN handle accesses.
+	*/
+
+	if (retcode != 0) {	/* unmount was unsuccessful */
+		dm_change_fsys_entry(sb, DM_STATE_MOUNTED);
+	} else {
+		dm_change_fsys_entry(sb, DM_STATE_UNMOUNTED);
+	}
+
+	/* If the event wasn't in the filesystem dm_eventset_t, just remove
+	   the filesystem from the list of DMAPI filesystems and return.
+	*/
+
+	if (flags & DM_FLAGS_UNWANTED) {
+		if (retcode == 0)
+			dm_remove_fsys_entry(sb);
+		return;
+	}
+
+	/* Calculate the size of the event in bytes and allocate zeroed memory
+	   for it.
+	*/
+
+	tdp1 = dm_sb_data(sb, ip, vfsp_right);
+	if (tdp1 == NULL)
+		return;
+
+	tevp = dm_evt_create_tevp(DM_EVENT_UNMOUNT, HANDLE_SIZE(tdp1),
+		(void **)&np);
+	if (tevp == NULL) {
+		kmem_cache_free(dm_tokdata_cachep, tdp1);
+		return;
+	}
+
+	dm_add_handle_to_event(tevp, tdp1);
+
+	/* Now copy in all the dm_namesp_event_t specific fields. */
+
+	np->ne_handle1.vd_offset = sizeof(*np);
+	np->ne_handle1.vd_length = HANDLE_SIZE(tdp1);
+	memcpy((char *) np + np->ne_handle1.vd_offset, &tdp1->td_handle,
+			np->ne_handle1.vd_length);
+	np->ne_mode = mode;
+	np->ne_retcode = retcode;
+
+	/* Since DM_EVENT_UNMOUNT is effectively asynchronous, queue the
+	   message and ignore any error return for DM_EVENT_UNMOUNT.
+	*/
+
+	(void)dm_enqueue_normal_event(sb, &tevp, flags);
+
+	if (retcode == 0)
+		dm_remove_fsys_entry(sb);
+
+	dm_evt_rele_tevp(tevp, 0);
+}
+
+
+/* Generate the given namespace event and wait for a reply (if synchronous) or
+   until the event has been queued (asynchronous).  The caller must guarantee
+   that at least one inode within the filesystem has had its reference count
+   bumped so that the filesystem can't disappear while the event is
+   outstanding.
+*/
+
+int
+dm_send_namesp_event(
+	dm_eventtype_t	event,
+	struct super_block *sb,		/* used by PREUNMOUNT */
+	struct inode	*ip1,
+	dm_right_t	vp1_right,
+	struct inode	*ip2,
+	dm_right_t	vp2_right,
+	const char	*name1,
+	const char	*name2,
+	mode_t		mode,
+	int		retcode,
+	int		flags)
+{
+	dm_namesp_event_t	*np;
+	dm_tokevent_t	*tevp;
+	dm_tokdata_t	*tdp1 = NULL;	/* primary handle for event */
+	dm_tokdata_t	*tdp2 = NULL;	/* additional handle for event */
+	size_t		nextoff;
+	int		error;
+
+	if (sb == NULL)
+		sb = ip1->i_sb;
+
+	switch (event) {
+	case DM_EVENT_PREUNMOUNT:
+		/*
+		 *  PREUNMOUNT - Send the file system handle in handle1,
+		 *  and the handle for the root dir in the second.  Otherwise
+		 *  it's a normal sync message; i.e. succeeds or fails
+		 *  depending on the app's return code.
+		 *	ip1 and ip2 are both the root dir of mounted FS
+		 *	vp1_right is the filesystem right.
+		 *	vp2_right is the root inode right.
+		 */
+
+		if (flags & DM_FLAGS_UNWANTED) {
+			dm_change_fsys_entry(sb, DM_STATE_UNMOUNTING);
+			return(0);
+		}
+		if (ip1 == NULL) {
+			/* If preunmount happens after kill_super then
+			 * it's too late; there's nothing left with which
+			 * to construct an event.
+			 */
+			return(0);
+		}
+		tdp1 = dm_sb_data(sb, ip1, vp1_right);
+		if (tdp1 == NULL)
+			return -ENOMEM;
+		tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1);
+		if (tdp2 == NULL) {
+			kmem_cache_free(dm_tokdata_cachep, tdp1);
+			return -ENOMEM;
+		}
+		break;
+
+	case DM_EVENT_NOSPACE:
+		/* vp1_right is the filesystem right. */
+
+		tdp1 = dm_sb_data(sb, ip1, vp1_right);
+		if (tdp1 == NULL)
+			return -ENOMEM;
+		tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1); /* additional info - not in the spec */
+		if (tdp2 == NULL) {
+			kmem_cache_free(dm_tokdata_cachep, tdp1);
+			return -ENOMEM;
+		}
+		break;
+
+	default:
+		/* All other events only pass in inodes and don't require any
+		   special cases.
+		*/
+
+		tdp1 = dm_ip_data(ip1, vp1_right, /* reference held */ 1);
+		if (tdp1 == NULL)
+			return -ENOMEM;
+		if (ip2) {
+			tdp2 = dm_ip_data(ip2, vp2_right, /* reference held */ 1);
+			if (tdp2 == NULL) {
+				kmem_cache_free(dm_tokdata_cachep, tdp1);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	/* Calculate the size of the event in bytes and allocate zeroed memory
+	   for it.
+	*/
+
+	tevp = dm_evt_create_tevp(event,
+		HANDLE_SIZE(tdp1) + (ip2 ? HANDLE_SIZE(tdp2) : 0) +
+		(name1 ? strlen(name1) + 1 : 0) +
+		(name2 ? strlen(name2) + 1 : 0), (void **)&np);
+	if (tevp == NULL) {
+		if (tdp1)
+			kmem_cache_free(dm_tokdata_cachep, tdp1);
+		if (tdp2)
+			kmem_cache_free(dm_tokdata_cachep, tdp2);
+		return(-ENOMEM);
+	}
+
+	dm_add_handle_to_event(tevp, tdp1);
+	if (ip2)
+		dm_add_handle_to_event(tevp, tdp2);
+
+	/* Now copy in all the dm_namesp_event_t specific fields. */
+
+	np->ne_handle1.vd_offset = sizeof(*np);
+	np->ne_handle1.vd_length = HANDLE_SIZE(tdp1);
+	memcpy((char *) np + np->ne_handle1.vd_offset, &tdp1->td_handle,
+			np->ne_handle1.vd_length);
+	nextoff = GETNEXTOFF(np->ne_handle1);
+	if (ip2) {
+		np->ne_handle2.vd_offset = nextoff;
+		np->ne_handle2.vd_length = HANDLE_SIZE(tdp2);
+		memcpy((char *)np + np->ne_handle2.vd_offset, &tdp2->td_handle,
+				np->ne_handle2.vd_length);
+		nextoff = GETNEXTOFF(np->ne_handle2);
+	}
+	if (name1) {
+		np->ne_name1.vd_offset = nextoff;
+		np->ne_name1.vd_length = strlen(name1) + 1;
+		memcpy((char *)np + np->ne_name1.vd_offset, name1,
+				np->ne_name1.vd_length);
+		nextoff = GETNEXTOFF(np->ne_name1);
+	}
+	if (name2) {
+		np->ne_name2.vd_offset = nextoff;
+		np->ne_name2.vd_length = strlen(name2) + 1;
+		memcpy((char *)np + np->ne_name2.vd_offset, name2,
+				np->ne_name2.vd_length);
+	}
+	np->ne_mode = mode;
+	np->ne_retcode = retcode;
+
+	/* Queue the message and wait for the reply. */
+
+	error = dm_enqueue_normal_event(sb, &tevp, flags);
+
+	/* If no errors occurred, we must leave with the same rights we had
+	   upon entry.	If errors occurred, we must leave with no rights.
+	*/
+
+	dm_evt_rele_tevp(tevp, error);
+
+	if (!error && event == DM_EVENT_PREUNMOUNT) {
+		dm_change_fsys_entry(sb, DM_STATE_UNMOUNTING);
+	}
+
+	return(error);
+}
+
+
+/*
+ *  Send a message of type "DM_EVENT_USER".  Since no inode is involved, we
+ *  don't have to worry about rights here.
+ */
+
+int
+dm_send_msg(
+	dm_sessid_t	targetsid,
+	dm_msgtype_t	msgtype,		/* SYNC or ASYNC */
+	size_t		buflen,
+	void		__user *bufp)
+{
+	dm_tokevent_t	*tevp;
+	int		sync;
+	void		*msgp;
+	int		error;
+
+	if (buflen > DM_MAX_MSG_DATA)
+		return(-E2BIG);
+	if (msgtype == DM_MSGTYPE_ASYNC) {
+		sync = 0;
+	} else if (msgtype == DM_MSGTYPE_SYNC) {
+		sync = 1;
+	} else {
+		return(-EINVAL);
+	}
+
+	tevp = dm_evt_create_tevp(DM_EVENT_USER, buflen, (void **)&msgp);
+	if (tevp == NULL)
+		return -ENOMEM;
+
+	if (buflen && copy_from_user(msgp, bufp, buflen)) {
+		dm_evt_rele_tevp(tevp, 0);
+		return(-EFAULT);
+	}
+
+	/* Enqueue the request and wait for the reply. */
+
+	error = dm_enqueue_sendmsg_event(targetsid, tevp, sync);
+
+	/* Destroy the tevp and return the reply.  (dm_pending is not
+	   supported here.)
+	*/
+
+	dm_evt_rele_tevp(tevp, error);
+
+	return(error);
+}
+
+
+/*
+ *  Send a message of type "DM_EVENT_USER".  Since no inode is involved, we
+ *  don't have to worry about rights here.
+ */
+
+int
+dm_create_userevent(
+	dm_sessid_t	sid,
+	size_t		msglen,
+	void		__user *msgdatap,
+	dm_token_t	__user *tokenp)		/* return token created */
+{
+	dm_tokevent_t	*tevp;
+	dm_token_t	token;
+	int		error;
+	void		*msgp;
+
+	if (msglen > DM_MAX_MSG_DATA)
+		return(-E2BIG);
+
+	tevp = dm_evt_create_tevp(DM_EVENT_USER, msglen, (void **)&msgp);
+	if (tevp == NULL)
+		return(-ENOMEM);
+
+	if (msglen && copy_from_user(msgp, msgdatap, msglen)) {
+		dm_evt_rele_tevp(tevp, 0);
+		return(-EFAULT);
+	}
+
+	/* Queue the message.  If that didn't work, free the tevp structure. */
+
+	if ((error = dm_enqueue_user_event(sid, tevp, &token)) != 0)
+		dm_evt_rele_tevp(tevp, 0);
+
+	if (!error && copy_to_user(tokenp, &token, sizeof(token)))
+		error = -EFAULT;
+
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_handle.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_create_by_handle(
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->create_by_handle(tdp->td_ip, tdp->td_right,
+		hanp, hlen, cname);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_mkdir_by_handle(
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->mkdir_by_handle(tdp->td_ip, tdp->td_right,
+		hanp, hlen, cname);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_symlink_by_handle(
+	dm_sessid_t	sid,
+	void		__user *dirhanp,
+	size_t		dirhlen,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname,
+	char		__user *path)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, dirhanp, dirhlen, token, DM_TDT_DIR,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->symlink_by_handle(tdp->td_ip, tdp->td_right,
+		hanp, hlen, cname, path);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_hole.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_get_allocinfo_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	__user	*offp,
+	u_int		nelem,
+	dm_extent_t	__user *extentp,
+	u_int		__user *nelemp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_allocinfo_rvp(tdp->td_ip, tdp->td_right,
+		offp, nelem, extentp, nelemp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_probe_hole(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len,
+	dm_off_t	__user	*roffp,
+	dm_size_t	__user *rlenp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->probe_hole(tdp->td_ip, tdp->td_right,
+		off, len, roffp, rlenp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_punch_hole(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->punch_hole(tdp->td_ip, tdp->td_right, off, len);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_io.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_read_invis_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		__user *bufp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->read_invis_rvp(tdp->td_ip, tdp->td_right,
+		off, len, bufp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_write_invis_rvp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	int		flags,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		__user *bufp,
+	int		*rvp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->write_invis_rvp(tdp->td_ip, tdp->td_right,
+		flags, off, len, bufp, rvp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_sync_by_handle (
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->sync_by_handle(tdp->td_ip, tdp->td_right);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_get_dioinfo (
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_dioinfo_t	__user *diop)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_dioinfo(tdp->td_ip, tdp->td_right, diop);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_kern.h
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#ifndef __DMAPI_KERN_H__
+#define __DMAPI_KERN_H__
+
+#include <linux/fs.h>
+
+union sys_dmapi_uarg {
+	void *p;
+	__u64 u;
+};
+typedef union sys_dmapi_uarg sys_dmapi_u;
+
+struct sys_dmapi_args {
+	sys_dmapi_u uarg1, uarg2, uarg3, uarg4, uarg5, uarg6, uarg7, uarg8,
+		uarg9, uarg10, uarg11;
+};
+typedef struct sys_dmapi_args sys_dmapi_args_t;
+
+#define DM_Uarg(uap,i)	uap->uarg##i.u
+#define DM_Parg(uap,i)	uap->uarg##i.p
+
+#ifdef __KERNEL__
+
+struct dm_handle_t;
+
+/* The first group of definitions and prototypes define the filesystem's
+   interface into the DMAPI code.
+*/
+
+
+/* Definitions used for the flags field on dm_send_data_event(),
+   dm_send_unmount_event(), and dm_send_namesp_event() calls.
+*/
+
+#define DM_FLAGS_NDELAY		0x001	/* return EAGAIN after dm_pending() */
+#define DM_FLAGS_UNWANTED	0x002	/* event not in fsys dm_eventset_t */
+
+/* Possible code levels reported by dm_code_level(). */
+
+#define DM_CLVL_INIT	0	/* DMAPI prior to X/Open compliance */
+#define DM_CLVL_XOPEN	1	/* X/Open compliant DMAPI */
+
+
+/*
+ * Filesystem operations accessed by the DMAPI core.
+ */
+struct filesystem_dmapi_operations {
+	int (*get_fsys_vector)(struct super_block *sb, void *addr);
+	int (*fh_to_inode)(struct super_block *sb, struct inode **ip,
+			   dm_fid_t *fid);
+	int (*inode_to_fh)(struct inode *ip, dm_fid_t *fid,
+			   dm_fsid_t *fsid );
+	void (*get_fsid)(struct super_block *sb, dm_fsid_t *fsid);
+#define HAVE_DM_QUEUE_FLUSH
+	int (*flushing)(struct inode *ip);
+};
+
+
+/* Prototypes used outside of the DMI module/directory. */
+
+int		dm_send_data_event(
+			dm_eventtype_t	event,
+			struct inode	*ip,
+			dm_right_t	vp_right,
+			dm_off_t	off,
+			size_t		len,
+			int		flags);
+
+int		dm_send_destroy_event(
+			struct inode	*ip,
+			dm_right_t	vp_right);
+
+int		dm_send_mount_event(
+			struct super_block	*sb,
+			dm_right_t	vfsp_right,
+			struct inode	*ip,
+			dm_right_t	vp_right,
+			struct inode	*rootip,
+			dm_right_t	rootvp_right,
+			char		*name1,
+			char		*name2);
+
+int		dm_send_namesp_event(
+			dm_eventtype_t	event,
+			struct super_block	*sb,
+			struct inode	*ip1,
+			dm_right_t	vp1_right,
+			struct inode	*ip2,
+			dm_right_t	vp2_right,
+			const char	*name1,
+			const char	*name2,
+			mode_t		mode,
+			int		retcode,
+			int		flags);
+
+void		dm_send_unmount_event(
+			struct super_block *sbp,
+			struct inode	*ip,
+			dm_right_t	sbp_right,
+			mode_t		mode,
+			int		retcode,
+			int		flags);
+
+int		dm_code_level(void);
+
+int		dm_ip_to_handle (
+			struct inode	*ip,
+			dm_handle_t	*handlep);
+
+#define HAVE_DM_RELEASE_THREADS_ERRNO
+int		dm_release_threads(
+			struct super_block	*sb,
+			struct inode		*inode,
+			int			errno);
+
+void		dmapi_register(
+			struct file_system_type *fstype,
+			struct filesystem_dmapi_operations *dmapiops);
+
+void		dmapi_unregister(
+			struct file_system_type *fstype);
+
+int		dmapi_registered(
+			struct file_system_type *fstype,
+			struct filesystem_dmapi_operations **dmapiops);
+
+
+/* The following prototypes and definitions are used by DMAPI as its
+   interface into the filesystem code.	Communication between DMAPI and the
+   filesystem are established as follows:
+   1. DMAPI uses the VFS_DMAPI_FSYS_VECTOR to ask for the addresses
+      of all the functions within the filesystem that it may need to call.
+   2. The filesystem returns an array of function name/address pairs which
+      DMAPI builds into a function vector.
+   The VFS_DMAPI_FSYS_VECTOR call is only made one time for a particular
+   filesystem type.  From then on, DMAPI uses its function vector to call the
+   filesystem functions directly.  Functions in the array which DMAPI doesn't
+   recognize are ignored.  A dummy function which returns ENOSYS is used for
+   any function that DMAPI needs but which was not provided by the filesystem.
+   If XFS doesn't recognize the VFS_DMAPI_FSYS_VECTOR, DMAPI assumes that it
+   doesn't have the X/Open support code; in this case DMAPI uses the XFS-code
+   originally bundled within DMAPI.
+
+   The goal of this interface is allow incremental changes to be made to
+   both the filesystem and to DMAPI while minimizing inter-patch dependencies,
+   and to eventually allow DMAPI to support multiple filesystem types at the
+   same time should that become necessary.
+*/
+
+typedef enum {
+	DM_FSYS_CLEAR_INHERIT		=  0,
+	DM_FSYS_CREATE_BY_HANDLE	=  1,
+	DM_FSYS_DOWNGRADE_RIGHT		=  2,
+	DM_FSYS_GET_ALLOCINFO_RVP	=  3,
+	DM_FSYS_GET_BULKALL_RVP		=  4,
+	DM_FSYS_GET_BULKATTR_RVP	=  5,
+	DM_FSYS_GET_CONFIG		=  6,
+	DM_FSYS_GET_CONFIG_EVENTS	=  7,
+	DM_FSYS_GET_DESTROY_DMATTR	=  8,
+	DM_FSYS_GET_DIOINFO		=  9,
+	DM_FSYS_GET_DIRATTRS_RVP	= 10,
+	DM_FSYS_GET_DMATTR		= 11,
+	DM_FSYS_GET_EVENTLIST		= 12,
+	DM_FSYS_GET_FILEATTR		= 13,
+	DM_FSYS_GET_REGION		= 14,
+	DM_FSYS_GETALL_DMATTR		= 15,
+	DM_FSYS_GETALL_INHERIT		= 16,
+	DM_FSYS_INIT_ATTRLOC		= 17,
+	DM_FSYS_MKDIR_BY_HANDLE		= 18,
+	DM_FSYS_PROBE_HOLE		= 19,
+	DM_FSYS_PUNCH_HOLE		= 20,
+	DM_FSYS_READ_INVIS_RVP		= 21,
+	DM_FSYS_RELEASE_RIGHT		= 22,
+	DM_FSYS_REMOVE_DMATTR		= 23,
+	DM_FSYS_REQUEST_RIGHT		= 24,
+	DM_FSYS_SET_DMATTR		= 25,
+	DM_FSYS_SET_EVENTLIST		= 26,
+	DM_FSYS_SET_FILEATTR		= 27,
+	DM_FSYS_SET_INHERIT		= 28,
+	DM_FSYS_SET_REGION		= 29,
+	DM_FSYS_SYMLINK_BY_HANDLE	= 30,
+	DM_FSYS_SYNC_BY_HANDLE		= 31,
+	DM_FSYS_UPGRADE_RIGHT		= 32,
+	DM_FSYS_WRITE_INVIS_RVP		= 33,
+	DM_FSYS_OBJ_REF_HOLD		= 34,
+	DM_FSYS_MAX			= 35
+} dm_fsys_switch_t;
+
+
+#define DM_FSYS_OBJ	0x1		/* object refers to a fsys handle */
+
+
+/*
+ *  Prototypes for filesystem-specific functions.
+ */
+
+typedef int	(*dm_fsys_clear_inherit_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_attrname_t	__user *attrnamep);
+
+typedef int	(*dm_fsys_create_by_handle_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			void		__user *hanp,
+			size_t		hlen,
+			char		__user *cname);
+
+typedef int	(*dm_fsys_downgrade_right_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type);		/* DM_FSYS_OBJ or zero */
+
+typedef int	(*dm_fsys_get_allocinfo_rvp_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_off_t	__user	*offp,
+			u_int		nelem,
+			dm_extent_t	__user *extentp,
+			u_int		__user *nelemp,
+			int		*rvalp);
+
+typedef int	(*dm_fsys_get_bulkall_rvp_t)(
+			struct inode	*ip,		/* root inode */
+			dm_right_t	right,
+			u_int		mask,
+			dm_attrname_t	__user *attrnamep,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp,
+			int		*rvalp);
+
+typedef int	(*dm_fsys_get_bulkattr_rvp_t)(
+			struct inode	*ip,		/* root inode */
+			dm_right_t	right,
+			u_int		mask,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp,
+			int		*rvalp);
+
+typedef int	(*dm_fsys_get_config_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_config_t	flagname,
+			dm_size_t	__user *retvalp);
+
+typedef int	(*dm_fsys_get_config_events_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		nelem,
+			dm_eventset_t	__user *eventsetp,
+			u_int		__user *nelemp);
+
+typedef int	(*dm_fsys_get_destroy_dmattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_attrname_t	*attrnamep,
+			char		**valuepp,
+			int		*vlenp);
+
+typedef int	(*dm_fsys_get_dioinfo_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_dioinfo_t	__user *diop);
+
+typedef int	(*dm_fsys_get_dirattrs_rvp_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		mask,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user  *bufp,
+			size_t		__user *rlenp,
+			int		*rvalp);
+
+typedef int	(*dm_fsys_get_dmattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_attrname_t	__user *attrnamep,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp);
+
+typedef int	(*dm_fsys_get_eventlist_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type,
+			u_int		nelem,
+			dm_eventset_t	*eventsetp,	/* in kernel space! */
+			u_int		*nelemp);	/* in kernel space! */
+
+typedef int	(*dm_fsys_get_fileattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		mask,
+			dm_stat_t	__user *statp);
+
+typedef int	(*dm_fsys_get_region_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		nelem,
+			dm_region_t	__user *regbufp,
+			u_int		__user *nelemp);
+
+typedef int	(*dm_fsys_getall_dmattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp);
+
+typedef int	(*dm_fsys_getall_inherit_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		nelem,
+			dm_inherit_t	__user *inheritbufp,
+			u_int __user	*nelemp);
+
+typedef int	(*dm_fsys_init_attrloc_t)(
+			struct inode	*ip,	/* sometimes root inode */
+			dm_right_t	right,
+			dm_attrloc_t	__user *locp);
+
+typedef int	(*dm_fsys_mkdir_by_handle_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			void		__user *hanp,
+			size_t		hlen,
+			char		__user *cname);
+
+typedef int	(*dm_fsys_probe_hole_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_off_t	off,
+			dm_size_t	len,
+			dm_off_t	__user	*roffp,
+			dm_size_t	__user *rlenp);
+
+typedef int	(*dm_fsys_punch_hole_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_off_t	off,
+			dm_size_t	len);
+
+typedef int	(*dm_fsys_read_invis_rvp_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_off_t	off,
+			dm_size_t	len,
+			void		__user *bufp,
+			int		*rvp);
+
+typedef int	(*dm_fsys_release_right_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type);
+
+typedef int	(*dm_fsys_remove_dmattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			int		setdtime,
+			dm_attrname_t	__user *attrnamep);
+
+typedef int	(*dm_fsys_request_right_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type,	/* DM_FSYS_OBJ or zero */
+			u_int		flags,
+			dm_right_t	newright);
+
+typedef int	(*dm_fsys_set_dmattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_attrname_t	__user *attrnamep,
+			int		setdtime,
+			size_t		buflen,
+			void		__user *bufp);
+
+typedef int	(*dm_fsys_set_eventlist_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type,
+			dm_eventset_t	*eventsetp,	/* in kernel space! */
+			u_int		maxevent);
+
+typedef int	(*dm_fsys_set_fileattr_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		mask,
+			dm_fileattr_t	__user *attrp);
+
+typedef int	(*dm_fsys_set_inherit_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			dm_attrname_t	__user *attrnamep,
+			mode_t		mode);
+
+typedef int	(*dm_fsys_set_region_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		nelem,
+			dm_region_t	__user *regbufp,
+			dm_boolean_t	__user *exactflagp);
+
+typedef int	(*dm_fsys_symlink_by_handle_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			void		__user *hanp,
+			size_t		hlen,
+			char		__user *cname,
+			char		__user *path);
+
+typedef int	(*dm_fsys_sync_by_handle_t)(
+			struct inode	*ip,
+			dm_right_t	right);
+
+typedef int	(*dm_fsys_upgrade_right_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			u_int		type);	/* DM_FSYS_OBJ or zero */
+
+typedef int	(*dm_fsys_write_invis_rvp_t)(
+			struct inode	*ip,
+			dm_right_t	right,
+			int		flags,
+			dm_off_t	off,
+			dm_size_t	len,
+			void		__user *bufp,
+			int		*rvp);
+
+typedef void	(*dm_fsys_obj_ref_hold_t)(
+			struct inode	*ip);
+
+
+/* Structure definitions used by the VFS_DMAPI_FSYS_VECTOR call. */
+
+typedef struct {
+	dm_fsys_switch_t  func_no;	/* function number */
+	union {
+		dm_fsys_clear_inherit_t clear_inherit;
+		dm_fsys_create_by_handle_t create_by_handle;
+		dm_fsys_downgrade_right_t downgrade_right;
+		dm_fsys_get_allocinfo_rvp_t get_allocinfo_rvp;
+		dm_fsys_get_bulkall_rvp_t get_bulkall_rvp;
+		dm_fsys_get_bulkattr_rvp_t get_bulkattr_rvp;
+		dm_fsys_get_config_t get_config;
+		dm_fsys_get_config_events_t get_config_events;
+		dm_fsys_get_destroy_dmattr_t get_destroy_dmattr;
+		dm_fsys_get_dioinfo_t get_dioinfo;
+		dm_fsys_get_dirattrs_rvp_t get_dirattrs_rvp;
+		dm_fsys_get_dmattr_t get_dmattr;
+		dm_fsys_get_eventlist_t get_eventlist;
+		dm_fsys_get_fileattr_t get_fileattr;
+		dm_fsys_get_region_t get_region;
+		dm_fsys_getall_dmattr_t getall_dmattr;
+		dm_fsys_getall_inherit_t getall_inherit;
+		dm_fsys_init_attrloc_t init_attrloc;
+		dm_fsys_mkdir_by_handle_t mkdir_by_handle;
+		dm_fsys_probe_hole_t probe_hole;
+		dm_fsys_punch_hole_t punch_hole;
+		dm_fsys_read_invis_rvp_t read_invis_rvp;
+		dm_fsys_release_right_t release_right;
+		dm_fsys_remove_dmattr_t remove_dmattr;
+		dm_fsys_request_right_t request_right;
+		dm_fsys_set_dmattr_t set_dmattr;
+		dm_fsys_set_eventlist_t set_eventlist;
+		dm_fsys_set_fileattr_t set_fileattr;
+		dm_fsys_set_inherit_t set_inherit;
+		dm_fsys_set_region_t set_region;
+		dm_fsys_symlink_by_handle_t symlink_by_handle;
+		dm_fsys_sync_by_handle_t sync_by_handle;
+		dm_fsys_upgrade_right_t upgrade_right;
+		dm_fsys_write_invis_rvp_t write_invis_rvp;
+		dm_fsys_obj_ref_hold_t obj_ref_hold;
+	} u_fc;
+} fsys_function_vector_t;
+
+struct dm_fcntl_vector {
+	int	code_level;
+	int	count;		/* Number of functions in the vector */
+	fsys_function_vector_t *vecp;
+};
+typedef struct dm_fcntl_vector dm_fcntl_vector_t;
+
+struct dm_fcntl_mapevent {
+	size_t	length;			/* length of transfer */
+	dm_eventtype_t	max_event;	/* Maximum (WRITE or READ)  event */
+	int	error;			/* returned error code */
+};
+typedef struct dm_fcntl_mapevent dm_fcntl_mapevent_t;
+
+#endif	/* __KERNEL__ */
+
+
+/* The following definitions are needed both by the kernel and by the
+   library routines.
+*/
+
+#define DM_MAX_HANDLE_SIZE	56	/* maximum size for a file handle */
+
+
+/*
+ *  Opcodes for dmapi ioctl.
+ */
+
+#define DM_CLEAR_INHERIT	1
+#define DM_CREATE_BY_HANDLE	2
+#define DM_CREATE_SESSION	3
+#define DM_CREATE_USEREVENT	4
+#define DM_DESTROY_SESSION	5
+#define DM_DOWNGRADE_RIGHT	6
+#define DM_FD_TO_HANDLE		7
+#define DM_FIND_EVENTMSG	8
+#define DM_GET_ALLOCINFO	9
+#define DM_GET_BULKALL		10
+#define DM_GET_BULKATTR		11
+#define DM_GET_CONFIG		12
+#define DM_GET_CONFIG_EVENTS	13
+#define DM_GET_DIOINFO		14
+#define DM_GET_DIRATTRS		15
+#define DM_GET_DMATTR		16
+#define DM_GET_EVENTLIST	17
+#define DM_GET_EVENTS		18
+#define DM_GET_FILEATTR		19
+#define DM_GET_MOUNTINFO	20
+#define DM_GET_REGION		21
+#define DM_GETALL_DISP		22
+#define DM_GETALL_DMATTR	23
+#define DM_GETALL_INHERIT	24
+#define DM_GETALL_SESSIONS	25
+#define DM_GETALL_TOKENS	26
+#define DM_INIT_ATTRLOC		27
+#define DM_MKDIR_BY_HANDLE	28
+#define DM_MOVE_EVENT		29
+#define DM_OBJ_REF_HOLD		30
+#define DM_OBJ_REF_QUERY	31
+#define DM_OBJ_REF_RELE		32
+#define DM_PATH_TO_FSHANDLE	33
+#define DM_PATH_TO_HANDLE	34
+#define DM_PENDING		35
+#define DM_PROBE_HOLE		36
+#define DM_PUNCH_HOLE		37
+#define DM_QUERY_RIGHT		38
+#define DM_QUERY_SESSION	39
+#define DM_READ_INVIS		40
+#define DM_RELEASE_RIGHT	41
+#define DM_REMOVE_DMATTR	42
+#define DM_REQUEST_RIGHT	43
+#define DM_RESPOND_EVENT	44
+#define DM_SEND_MSG		45
+#define DM_SET_DISP		46
+#define DM_SET_DMATTR		47
+#define DM_SET_EVENTLIST	48
+#define DM_SET_FILEATTR		49
+#define DM_SET_INHERIT		50
+#define DM_SET_REGION		51
+#define DM_SET_RETURN_ON_DESTROY 52
+#define DM_SYMLINK_BY_HANDLE	53
+#define DM_SYNC_BY_HANDLE	54
+#define DM_UPGRADE_RIGHT	55
+#define DM_WRITE_INVIS		56
+#define DM_OPEN_BY_HANDLE	57
+
+#endif /* __DMAPI_KERN_H__ */
--- /dev/null
+++ b/fs/dmapi/dmapi_mountinfo.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+static LIST_HEAD(dm_fsys_map);
+static spinlock_t dm_fsys_lock = SPIN_LOCK_UNLOCKED;
+
+int
+dm_code_level(void)
+{
+	return DM_CLVL_XOPEN;	/* initial X/Open compliant release */
+}
+
+
+/* Dummy routine which is stored in each function vector slot for which the
+   filesystem provides no function of its own.	If an application calls the
+   function, he will just get ENOSYS.
+*/
+
+static int
+dm_enosys(void)
+{
+	return -ENOSYS;		/* function not supported by filesystem */
+}
+
+
+/* dm_query_fsys_for_vector() asks a filesystem for its list of supported
+   DMAPI functions, and builds a dm_vector_map_t structure based upon the
+   reply.  We ignore functions supported by the filesystem which we do not
+   know about, and we substitute the subroutine 'dm_enosys' for each function
+   we know about but the filesystem does not support.
+*/
+
+static void
+dm_query_fsys_for_vector(
+	dm_vector_map_t		*map)
+{
+	struct super_block	*sb = map->sb;
+	fsys_function_vector_t	*vecp;
+	dm_fcntl_vector_t	vecrq;
+	dm_fsys_vector_t	*vptr;
+	struct filesystem_dmapi_operations *dmapiops = map->dmapiops;
+	int			error;
+	int			i;
+
+
+	/* Allocate a function vector and initialize all fields with a
+	   dummy function that returns ENOSYS.
+	*/
+
+	vptr = map->vptr = kmem_cache_alloc(dm_fsys_vptr_cachep, GFP_KERNEL);
+	if (vptr == NULL) {
+		printk("%s/%d: kmem_cache_alloc(dm_fsys_vptr_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+		return;
+	}
+
+	vptr->code_level = 0;
+	vptr->clear_inherit = (dm_fsys_clear_inherit_t)dm_enosys;
+	vptr->create_by_handle = (dm_fsys_create_by_handle_t)dm_enosys;
+	vptr->downgrade_right = (dm_fsys_downgrade_right_t)dm_enosys;
+	vptr->get_allocinfo_rvp = (dm_fsys_get_allocinfo_rvp_t)dm_enosys;
+	vptr->get_bulkall_rvp = (dm_fsys_get_bulkall_rvp_t)dm_enosys;
+	vptr->get_bulkattr_rvp = (dm_fsys_get_bulkattr_rvp_t)dm_enosys;
+	vptr->get_config = (dm_fsys_get_config_t)dm_enosys;
+	vptr->get_config_events = (dm_fsys_get_config_events_t)dm_enosys;
+	vptr->get_destroy_dmattr = (dm_fsys_get_destroy_dmattr_t)dm_enosys;
+	vptr->get_dioinfo = (dm_fsys_get_dioinfo_t)dm_enosys;
+	vptr->get_dirattrs_rvp = (dm_fsys_get_dirattrs_rvp_t)dm_enosys;
+	vptr->get_dmattr = (dm_fsys_get_dmattr_t)dm_enosys;
+	vptr->get_eventlist = (dm_fsys_get_eventlist_t)dm_enosys;
+	vptr->get_fileattr = (dm_fsys_get_fileattr_t)dm_enosys;
+	vptr->get_region = (dm_fsys_get_region_t)dm_enosys;
+	vptr->getall_dmattr = (dm_fsys_getall_dmattr_t)dm_enosys;
+	vptr->getall_inherit = (dm_fsys_getall_inherit_t)dm_enosys;
+	vptr->init_attrloc = (dm_fsys_init_attrloc_t)dm_enosys;
+	vptr->mkdir_by_handle = (dm_fsys_mkdir_by_handle_t)dm_enosys;
+	vptr->probe_hole = (dm_fsys_probe_hole_t)dm_enosys;
+	vptr->punch_hole = (dm_fsys_punch_hole_t)dm_enosys;
+	vptr->read_invis_rvp = (dm_fsys_read_invis_rvp_t)dm_enosys;
+	vptr->release_right = (dm_fsys_release_right_t)dm_enosys;
+	vptr->request_right = (dm_fsys_request_right_t)dm_enosys;
+	vptr->remove_dmattr = (dm_fsys_remove_dmattr_t)dm_enosys;
+	vptr->set_dmattr = (dm_fsys_set_dmattr_t)dm_enosys;
+	vptr->set_eventlist = (dm_fsys_set_eventlist_t)dm_enosys;
+	vptr->set_fileattr = (dm_fsys_set_fileattr_t)dm_enosys;
+	vptr->set_inherit = (dm_fsys_set_inherit_t)dm_enosys;
+	vptr->set_region = (dm_fsys_set_region_t)dm_enosys;
+	vptr->symlink_by_handle = (dm_fsys_symlink_by_handle_t)dm_enosys;
+	vptr->sync_by_handle = (dm_fsys_sync_by_handle_t)dm_enosys;
+	vptr->upgrade_right = (dm_fsys_upgrade_right_t)dm_enosys;
+	vptr->write_invis_rvp = (dm_fsys_write_invis_rvp_t)dm_enosys;
+	vptr->obj_ref_hold = (dm_fsys_obj_ref_hold_t)dm_enosys;
+
+	/* Issue a call to the filesystem in order to obtain
+	   its vector of filesystem-specific DMAPI routines.
+	*/
+
+	vecrq.count = 0;
+	vecrq.vecp = NULL;
+
+	error = -ENOSYS;
+	ASSERT(dmapiops);
+	if (dmapiops->get_fsys_vector)
+		error = dmapiops->get_fsys_vector(sb, (caddr_t)&vecrq);
+
+	/* If we still have an error at this point, then the filesystem simply
+	   does not support DMAPI, so we give up with all functions set to
+	   ENOSYS.
+	*/
+
+	if (error || vecrq.count == 0) {
+		kmem_cache_free(dm_fsys_vptr_cachep, vptr);
+		map->vptr = NULL;
+		return;
+	}
+
+	/* The request succeeded and we were given a vector which we need to
+	   map to our current level.  Overlay the dummy function with every
+	   filesystem function we understand.
+	*/
+
+	vptr->code_level = vecrq.code_level;
+	vecp = vecrq.vecp;
+	for (i = 0; i < vecrq.count; i++) {
+		switch (vecp[i].func_no) {
+		case DM_FSYS_CLEAR_INHERIT:
+			vptr->clear_inherit = vecp[i].u_fc.clear_inherit;
+			break;
+		case DM_FSYS_CREATE_BY_HANDLE:
+			vptr->create_by_handle = vecp[i].u_fc.create_by_handle;
+			break;
+		case DM_FSYS_DOWNGRADE_RIGHT:
+			vptr->downgrade_right = vecp[i].u_fc.downgrade_right;
+			break;
+		case DM_FSYS_GET_ALLOCINFO_RVP:
+			vptr->get_allocinfo_rvp = vecp[i].u_fc.get_allocinfo_rvp;
+			break;
+		case DM_FSYS_GET_BULKALL_RVP:
+			vptr->get_bulkall_rvp = vecp[i].u_fc.get_bulkall_rvp;
+			break;
+		case DM_FSYS_GET_BULKATTR_RVP:
+			vptr->get_bulkattr_rvp = vecp[i].u_fc.get_bulkattr_rvp;
+			break;
+		case DM_FSYS_GET_CONFIG:
+			vptr->get_config = vecp[i].u_fc.get_config;
+			break;
+		case DM_FSYS_GET_CONFIG_EVENTS:
+			vptr->get_config_events = vecp[i].u_fc.get_config_events;
+			break;
+		case DM_FSYS_GET_DESTROY_DMATTR:
+			vptr->get_destroy_dmattr = vecp[i].u_fc.get_destroy_dmattr;
+			break;
+		case DM_FSYS_GET_DIOINFO:
+			vptr->get_dioinfo = vecp[i].u_fc.get_dioinfo;
+			break;
+		case DM_FSYS_GET_DIRATTRS_RVP:
+			vptr->get_dirattrs_rvp = vecp[i].u_fc.get_dirattrs_rvp;
+			break;
+		case DM_FSYS_GET_DMATTR:
+			vptr->get_dmattr = vecp[i].u_fc.get_dmattr;
+			break;
+		case DM_FSYS_GET_EVENTLIST:
+			vptr->get_eventlist = vecp[i].u_fc.get_eventlist;
+			break;
+		case DM_FSYS_GET_FILEATTR:
+			vptr->get_fileattr = vecp[i].u_fc.get_fileattr;
+			break;
+		case DM_FSYS_GET_REGION:
+			vptr->get_region = vecp[i].u_fc.get_region;
+			break;
+		case DM_FSYS_GETALL_DMATTR:
+			vptr->getall_dmattr = vecp[i].u_fc.getall_dmattr;
+			break;
+		case DM_FSYS_GETALL_INHERIT:
+			vptr->getall_inherit = vecp[i].u_fc.getall_inherit;
+			break;
+		case DM_FSYS_INIT_ATTRLOC:
+			vptr->init_attrloc = vecp[i].u_fc.init_attrloc;
+			break;
+		case DM_FSYS_MKDIR_BY_HANDLE:
+			vptr->mkdir_by_handle = vecp[i].u_fc.mkdir_by_handle;
+			break;
+		case DM_FSYS_PROBE_HOLE:
+			vptr->probe_hole = vecp[i].u_fc.probe_hole;
+			break;
+		case DM_FSYS_PUNCH_HOLE:
+			vptr->punch_hole = vecp[i].u_fc.punch_hole;
+			break;
+		case DM_FSYS_READ_INVIS_RVP:
+			vptr->read_invis_rvp = vecp[i].u_fc.read_invis_rvp;
+			break;
+		case DM_FSYS_RELEASE_RIGHT:
+			vptr->release_right = vecp[i].u_fc.release_right;
+			break;
+		case DM_FSYS_REMOVE_DMATTR:
+			vptr->remove_dmattr = vecp[i].u_fc.remove_dmattr;
+			break;
+		case DM_FSYS_REQUEST_RIGHT:
+			vptr->request_right = vecp[i].u_fc.request_right;
+			break;
+		case DM_FSYS_SET_DMATTR:
+			vptr->set_dmattr = vecp[i].u_fc.set_dmattr;
+			break;
+		case DM_FSYS_SET_EVENTLIST:
+			vptr->set_eventlist = vecp[i].u_fc.set_eventlist;
+			break;
+		case DM_FSYS_SET_FILEATTR:
+			vptr->set_fileattr = vecp[i].u_fc.set_fileattr;
+			break;
+		case DM_FSYS_SET_INHERIT:
+			vptr->set_inherit = vecp[i].u_fc.set_inherit;
+			break;
+		case DM_FSYS_SET_REGION:
+			vptr->set_region = vecp[i].u_fc.set_region;
+			break;
+		case DM_FSYS_SYMLINK_BY_HANDLE:
+			vptr->symlink_by_handle = vecp[i].u_fc.symlink_by_handle;
+			break;
+		case DM_FSYS_SYNC_BY_HANDLE:
+			vptr->sync_by_handle = vecp[i].u_fc.sync_by_handle;
+			break;
+		case DM_FSYS_UPGRADE_RIGHT:
+			vptr->upgrade_right = vecp[i].u_fc.upgrade_right;
+			break;
+		case DM_FSYS_WRITE_INVIS_RVP:
+			vptr->write_invis_rvp = vecp[i].u_fc.write_invis_rvp;
+			break;
+		case DM_FSYS_OBJ_REF_HOLD:
+			vptr->obj_ref_hold = vecp[i].u_fc.obj_ref_hold;
+			break;
+		default:		/* ignore ones we don't understand */
+			break;
+		}
+	}
+}
+
+
+/* Must hold dm_fsys_lock.
+ * This returns the prototype for all instances of the fstype.
+ */
+static dm_vector_map_t *
+dm_fsys_map_by_fstype(
+	struct file_system_type *fstype)
+{
+	struct list_head *p;
+	dm_vector_map_t *proto = NULL;
+	dm_vector_map_t *m;
+
+	ASSERT_ALWAYS(fstype);
+	list_for_each(p, &dm_fsys_map) {
+		m = list_entry(p, dm_vector_map_t, ftype_list);
+		if (m->f_type == fstype) {
+			proto = m;
+			break;
+		}
+	}
+	return proto;
+}
+
+
+/* Must hold dm_fsys_lock */
+static dm_vector_map_t *
+dm_fsys_map_by_sb(
+	struct super_block *sb)
+{
+	struct list_head *p;
+	dm_vector_map_t *proto;
+	dm_vector_map_t *m;
+	dm_vector_map_t *foundmap = NULL;
+
+	proto = dm_fsys_map_by_fstype(sb->s_type);
+	if(proto == NULL) {
+		return NULL;
+	}
+
+	list_for_each(p, &proto->sb_list) {
+		m = list_entry(p, dm_vector_map_t, sb_list);
+		if (m->sb == sb) {
+			foundmap = m;
+			break;
+		}
+	}
+	return foundmap;
+}
+
+
+#ifdef CONFIG_DMAPI_DEBUG
+static void
+sb_list(
+	struct super_block *sb)
+{
+	struct list_head *p;
+	dm_vector_map_t *proto;
+	dm_vector_map_t *m;
+
+	proto = dm_fsys_map_by_fstype(sb->s_type);
+	ASSERT(proto);
+
+printk("%s/%d: Current sb_list\n", __FUNCTION__, __LINE__);
+	list_for_each(p, &proto->sb_list) {
+		m = list_entry(p, dm_vector_map_t, sb_list);
+printk("%s/%d: map 0x%p, sb 0x%p, vptr 0x%p, dmapiops 0x%p\n", __FUNCTION__, __LINE__, m, m->sb, m->vptr, m->dmapiops);
+	}
+printk("%s/%d: Done sb_list\n", __FUNCTION__, __LINE__);
+}
+#else
+#define sb_list(x)
+#endif
+
+#ifdef CONFIG_DMAPI_DEBUG
+static void
+ftype_list(void)
+{
+	struct list_head *p;
+	dm_vector_map_t *m;
+
+printk("%s/%d: Current ftype_list\n", __FUNCTION__, __LINE__);
+	list_for_each(p, &dm_fsys_map) {
+		m = list_entry(p, dm_vector_map_t, ftype_list);
+		printk("%s/%d: FS 0x%p, ftype 0x%p %s\n", __FUNCTION__, __LINE__, m, m->f_type, m->f_type->name);
+	}
+printk("%s/%d: Done ftype_list\n", __FUNCTION__, __LINE__);
+}
+#else
+#define ftype_list()
+#endif
+
+/* Ask for vptr for this filesystem instance.
+ * The caller knows this inode is on a dmapi-managed filesystem.
+ */
+dm_fsys_vector_t *
+dm_fsys_vector(
+	struct inode	*ip)
+{
+	dm_vector_map_t *map;
+
+	spin_lock(&dm_fsys_lock);
+	ftype_list();
+	map = dm_fsys_map_by_sb(ip->i_sb);
+	spin_unlock(&dm_fsys_lock);
+	ASSERT(map);
+	ASSERT(map->vptr);
+	return map->vptr;
+}
+
+
+/* Ask for the dmapiops for this filesystem instance.  The caller is
+ * also asking if this is a dmapi-managed filesystem.
+ */
+struct filesystem_dmapi_operations *
+dm_fsys_ops(
+	struct super_block	*sb)
+{
+	dm_vector_map_t *proto = NULL;
+	dm_vector_map_t *map;
+
+	spin_lock(&dm_fsys_lock);
+	ftype_list();
+	sb_list(sb);
+	map = dm_fsys_map_by_sb(sb);
+	if (map == NULL)
+		proto = dm_fsys_map_by_fstype(sb->s_type);
+	spin_unlock(&dm_fsys_lock);
+
+	if ((map == NULL) && (proto == NULL))
+		return NULL;
+
+	if (map == NULL) {
+		/* Find out if it's dmapi-managed */
+		dm_vector_map_t *m;
+
+		ASSERT(proto);
+		m = kmem_cache_alloc(dm_fsys_map_cachep, GFP_KERNEL);
+		if (m == NULL) {
+			printk("%s/%d: kmem_cache_alloc(dm_fsys_map_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+			return NULL;
+		}
+		memset(m, 0, sizeof(*m));
+		m->dmapiops = proto->dmapiops;
+		m->f_type = sb->s_type;
+		m->sb = sb;
+		INIT_LIST_HEAD(&m->sb_list);
+		INIT_LIST_HEAD(&m->ftype_list);
+
+		dm_query_fsys_for_vector(m);
+		if (m->vptr == NULL) {
+			/* This isn't dmapi-managed */
+			kmem_cache_free(dm_fsys_map_cachep, m);
+			return NULL;
+		}
+
+		spin_lock(&dm_fsys_lock);
+		if ((map = dm_fsys_map_by_sb(sb)) == NULL)
+			list_add(&m->sb_list, &proto->sb_list);
+		spin_unlock(&dm_fsys_lock);
+
+		if (map) {
+			kmem_cache_free(dm_fsys_vptr_cachep, m->vptr);
+			kmem_cache_free(dm_fsys_map_cachep, m);
+		}
+		else {
+			map = m;
+		}
+	}
+
+	return map->dmapiops;
+}
+
+
+
+/* Called when a filesystem instance is unregistered from dmapi */
+void
+dm_fsys_ops_release(
+	struct super_block	*sb)
+{
+	dm_vector_map_t *map;
+
+	spin_lock(&dm_fsys_lock);
+	ASSERT(!list_empty(&dm_fsys_map));
+	map = dm_fsys_map_by_sb(sb);
+	ASSERT(map);
+	list_del(&map->sb_list);
+	spin_unlock(&dm_fsys_lock);
+
+	ASSERT(map->vptr);
+	kmem_cache_free(dm_fsys_vptr_cachep, map->vptr);
+	kmem_cache_free(dm_fsys_map_cachep, map);
+}
+
+
+/* Called by a filesystem module that is loading into the kernel.
+ * This creates a new dm_vector_map_t which serves as the prototype
+ * for instances of this fstype and also provides the list_head
+ * for instances of this fstype.  The prototypes are the only ones
+ * on the fstype_list, and will never be on the sb_list.
+ */
+void
+dmapi_register(
+	struct file_system_type *fstype,
+	struct filesystem_dmapi_operations *dmapiops)
+{
+	dm_vector_map_t *proto;
+
+	proto = kmem_cache_alloc(dm_fsys_map_cachep, GFP_KERNEL);
+	if (proto == NULL) {
+		printk("%s/%d: kmem_cache_alloc(dm_fsys_map_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+		return;
+	}
+	memset(proto, 0, sizeof(*proto));
+	proto->dmapiops = dmapiops;
+	proto->f_type = fstype;
+	INIT_LIST_HEAD(&proto->sb_list);
+	INIT_LIST_HEAD(&proto->ftype_list);
+
+	spin_lock(&dm_fsys_lock);
+	ASSERT(dm_fsys_map_by_fstype(fstype) == NULL);
+	list_add(&proto->ftype_list, &dm_fsys_map);
+	ftype_list();
+	spin_unlock(&dm_fsys_lock);
+}
+
+/* Called by a filesystem module that is unloading from the kernel */
+void
+dmapi_unregister(
+	struct file_system_type *fstype)
+{
+	struct list_head *p;
+	dm_vector_map_t *proto;
+	dm_vector_map_t *m;
+
+	spin_lock(&dm_fsys_lock);
+	ASSERT(!list_empty(&dm_fsys_map));
+	proto = dm_fsys_map_by_fstype(fstype);
+	ASSERT(proto);
+	list_del(&proto->ftype_list);
+	spin_unlock(&dm_fsys_lock);
+
+	p = &proto->sb_list;
+	while (!list_empty(p)) {
+		m = list_entry(p->next, dm_vector_map_t, sb_list);
+		list_del(&m->sb_list);
+		ASSERT(m->vptr);
+		kmem_cache_free(dm_fsys_vptr_cachep, m->vptr);
+		kmem_cache_free(dm_fsys_map_cachep, m);
+	}
+	kmem_cache_free(dm_fsys_map_cachep, proto);
+}
+
+
+int
+dmapi_registered(
+	struct file_system_type *fstype,
+	struct filesystem_dmapi_operations **dmapiops)
+{
+	return 0;
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_port.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef _DMAPI_PORT_H
+#define _DMAPI_PORT_H
+
+#include <asm/div64.h>
+#include "sv.h"
+
+#include <linux/sched.h>	/* preempt needs this */
+#include <linux/spinlock.h>
+
+typedef spinlock_t lock_t;
+
+#define spinlock_init(lock, name)	spin_lock_init(lock)
+#define	spinlock_destroy(lock)
+
+#define mutex_spinlock(lock)		({ spin_lock(lock); 0; })
+#define mutex_spinunlock(lock, s)	spin_unlock(lock)
+#define nested_spinlock(lock)		spin_lock(lock)
+#define nested_spinunlock(lock)		spin_unlock(lock)
+
+typedef signed int		__int32_t;
+typedef unsigned int		__uint32_t;
+typedef signed long long int	__int64_t;
+typedef unsigned long long int	__uint64_t;
+
+
+/* __psint_t is the same size as a pointer */
+#if (BITS_PER_LONG == 32)
+typedef __int32_t __psint_t;
+typedef __uint32_t __psunsigned_t;
+#elif (BITS_PER_LONG == 64)
+typedef __int64_t __psint_t;
+typedef __uint64_t __psunsigned_t;
+#else
+#error BITS_PER_LONG must be 32 or 64
+#endif
+
+static inline void
+assfail(char *a, char *f, int l)
+{
+    printk("DMAPI assertion failed: %s, file: %s, line: %d\n", a, f, l);
+    BUG();
+}
+
+#ifdef DEBUG
+#define doass 1
+# ifdef lint
+#  define ASSERT(EX)	((void)0) /* avoid "constant in conditional" babble */
+# else
+#  define ASSERT(EX) ((!doass||(EX))?((void)0):assfail(#EX, __FILE__, __LINE__))
+# endif	/* lint */
+#else
+# define ASSERT(x)	((void)0)
+#endif /* DEBUG */
+
+#define ASSERT_ALWAYS(EX)  ((EX)?((void)0):assfail(#EX, __FILE__, __LINE__))
+
+
+#if defined __i386__
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 dmapi_do_mod(void *a, __u32 b, int n)
+{
+	switch (n) {
+		case 4:
+			return *(__u32 *)a % b;
+		case 8:
+			{
+			unsigned long __upper, __low, __high, __mod;
+			__u64	c = *(__u64 *)a;
+			__upper = __high = c >> 32;
+			__low = c;
+			if (__high) {
+				__upper = __high % (b);
+				__high = __high / (b);
+			}
+			asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (b), "0" (__low), "1" (__upper));
+			asm("":"=A" (c):"a" (__low),"d" (__high));
+			return __mod;
+			}
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+#else
+
+/* Side effect free 64 bit mod operation */
+static inline __u32 dmapi_do_mod(void *a, __u32 b, int n)
+{
+	switch (n) {
+		case 4:
+			return *(__u32 *)a % b;
+		case 8:
+			{
+			__u64	c = *(__u64 *)a;
+			return do_div(c, b);
+			}
+	}
+
+	/* NOTREACHED */
+	return 0;
+}
+#endif
+
+#define do_mod(a, b)	dmapi_do_mod(&(a), (b), sizeof(a))
+
+#endif /* _DMAPI_PORT_H */
--- /dev/null
+++ b/fs/dmapi/dmapi_private.h
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef _DMAPI_PRIVATE_H
+#define _DMAPI_PRIVATE_H
+
+#include <linux/slab.h>
+#include "dmapi_port.h"
+#include "sv.h"
+
+#ifdef CONFIG_PROC_FS
+#define DMAPI_PROCFS		"orig/fs/dmapi_v2" /* DMAPI device in /proc. */
+#define DMAPI_DBG_PROCFS	"orig/fs/dmapi_d" /* DMAPI debugging dir */
+#endif
+
+extern struct kmem_cache	*dm_fsreg_cachep;
+extern struct kmem_cache	*dm_tokdata_cachep;
+extern struct kmem_cache	*dm_session_cachep;
+extern struct kmem_cache	*dm_fsys_map_cachep;
+extern struct kmem_cache	*dm_fsys_vptr_cachep;
+
+typedef struct dm_tokdata {
+	struct dm_tokdata *td_next;
+	struct dm_tokevent *td_tevp;	/* pointer to owning tevp */
+	int		td_app_ref;	/* # app threads currently active */
+	dm_right_t	td_orig_right;	/* original right held when created */
+	dm_right_t	td_right;	/* current right held for this handle */
+	short		td_flags;
+	short		td_type;	/* object type */
+	int		td_vcount;	/* # of current application VN_HOLDs */
+	struct inode	*td_ip;		/* inode pointer */
+	dm_handle_t	td_handle;	/* handle for ip or sb */
+} dm_tokdata_t;
+
+/* values for td_type */
+
+#define DM_TDT_NONE	0x00		/* td_handle is empty */
+#define DM_TDT_VFS	0x01		/* td_handle points to a sb */
+#define DM_TDT_REG	0x02		/* td_handle points to a file */
+#define DM_TDT_DIR	0x04		/* td_handle points to a directory */
+#define DM_TDT_LNK	0x08		/* td_handle points to a symlink */
+#define DM_TDT_OTH	0x10		/* some other object eg. pipe, socket */
+
+#define DM_TDT_VNO	(DM_TDT_REG|DM_TDT_DIR|DM_TDT_LNK|DM_TDT_OTH)
+#define DM_TDT_ANY	(DM_TDT_VFS|DM_TDT_REG|DM_TDT_DIR|DM_TDT_LNK|DM_TDT_OTH)
+
+/* values for td_flags */
+
+#define DM_TDF_ORIG	0x0001		/* part of the original event */
+#define DM_TDF_EVTREF	0x0002		/* event thread holds inode reference */
+#define DM_TDF_STHREAD	0x0004		/* only one app can use this handle */
+#define DM_TDF_RIGHT	0x0008		/* vcount bumped for dm_request_right */
+#define DM_TDF_HOLD	0x0010		/* vcount bumped for dm_obj_ref_hold */
+
+
+/* Because some events contain __u64 fields, we force te_msg and te_event
+   to always be 8-byte aligned.	 In order to send more than one message in
+   a single dm_get_events() call, we also ensure that each message is an
+   8-byte multiple.
+*/
+
+typedef struct dm_tokevent {
+	struct dm_tokevent  *te_next;
+	struct dm_tokevent  *te_hashnext; /* hash chain */
+	lock_t		te_lock;	/* lock for all fields but te_*next.
+					 * te_next and te_hashnext are
+					 * protected by the session lock.
+					 */
+	short		te_flags;
+	short		te_allocsize;	/* alloc'ed size of this structure */
+	sv_t		te_evt_queue;	/* queue waiting for dm_respond_event */
+	sv_t		te_app_queue;	/* queue waiting for handle access */
+	int		te_evt_ref;	/* number of event procs using token */
+	int		te_app_ref;	/* number of app procs using token */
+	int		te_app_slp;	/* number of app procs sleeping */
+	int		te_reply;	/* return errno for sync messages */
+	dm_tokdata_t	*te_tdp;	/* list of handle/right pairs */
+	union {
+		__u64		align;	/* force alignment of te_msg */
+		dm_eventmsg_t	te_msg;		/* user visible part */
+	} te_u;
+	__u64		te_event;	/* start of dm_xxx_event_t message */
+} dm_tokevent_t;
+
+#define te_msg	te_u.te_msg
+
+/* values for te_flags */
+
+#define DM_TEF_LOCKED	0x0001		/* event "locked" by dm_get_events() */
+#define DM_TEF_INTERMED 0x0002		/* a dm_pending reply was received */
+#define DM_TEF_FINAL	0x0004		/* dm_respond_event has been received */
+#define DM_TEF_HASHED	0x0010		/* event is on hash chain  */
+#define DM_TEF_FLUSH	0x0020		/* flushing threads from queues */
+
+
+#ifdef CONFIG_DMAPI_DEBUG
+#define DM_SHASH_DEBUG
+#endif
+
+typedef struct dm_sesshash {
+	dm_tokevent_t	*h_next;	/* ptr to chain of tokevents */
+#ifdef DM_SHASH_DEBUG
+	int		maxlength;
+	int		curlength;
+	int		num_adds;
+	int		num_dels;
+	int		dup_hits;
+#endif
+} dm_sesshash_t;
+
+
+typedef struct dm_eventq {
+	dm_tokevent_t	*eq_head;
+	dm_tokevent_t	*eq_tail;
+	int		eq_count;	/* size of queue */
+} dm_eventq_t;
+
+
+typedef struct dm_session {
+	struct dm_session	*sn_next;	/* sessions linkage */
+	dm_sessid_t	sn_sessid;	/* user-visible session number */
+	u_int		sn_flags;
+	lock_t		sn_qlock;	/* lock for newq/delq related fields */
+	sv_t		sn_readerq;	/* waiting for message on sn_newq */
+	sv_t		sn_writerq;	/* waiting for room on sn_newq */
+	u_int		sn_readercnt;	/* count of waiting readers */
+	u_int		sn_writercnt;	/* count of waiting readers */
+	dm_eventq_t	sn_newq;	/* undelivered event queue */
+	dm_eventq_t	sn_delq;	/* delivered event queue */
+	dm_eventq_t	sn_evt_writerq; /* events of thrds in sn_writerq */
+	dm_sesshash_t	*sn_sesshash;	/* buckets for tokevent hash chains */
+#ifdef DM_SHASH_DEBUG
+	int		sn_buckets_in_use;
+	int		sn_max_buckets_in_use;
+#endif
+	char		sn_info[DM_SESSION_INFO_LEN];	/* user-supplied info */
+} dm_session_t;
+
+/* values for sn_flags */
+
+#define DM_SN_WANTMOUNT 0x0001		/* session wants to get mount events */
+
+
+typedef enum {
+	DM_STATE_MOUNTING,
+	DM_STATE_MOUNTED,
+	DM_STATE_UNMOUNTING,
+	DM_STATE_UNMOUNTED
+} dm_fsstate_t;
+
+
+typedef struct dm_fsreg {
+	struct dm_fsreg *fr_next;
+	struct super_block *fr_sb;	/* filesystem pointer */
+	dm_tokevent_t	*fr_tevp;
+	dm_fsid_t	fr_fsid;	/* filesystem ID */
+	void		*fr_msg;	/* dm_mount_event_t for filesystem */
+	int		fr_msgsize;	/* size of dm_mount_event_t */
+	dm_fsstate_t	fr_state;
+	sv_t		fr_dispq;
+	int		fr_dispcnt;
+	dm_eventq_t	fr_evt_dispq;	/* events of thrds in fr_dispq */
+	sv_t		fr_queue;	/* queue for hdlcnt/sbcnt/unmount */
+	lock_t		fr_lock;
+	int		fr_hdlcnt;	/* threads blocked during unmount */
+	int		fr_vfscnt;	/* threads in VFS_VGET or VFS_ROOT */
+	int		fr_unmount;	/* if non-zero, umount is sleeping */
+	dm_attrname_t	fr_rattr;	/* dm_set_return_on_destroy attribute */
+	dm_session_t	*fr_sessp [DM_EVENT_MAX];
+} dm_fsreg_t;
+
+
+
+
+/* events valid in dm_set_disp() when called with a filesystem handle. */
+
+#define DM_VALID_DISP_EVENTS		( \
+	(1 << DM_EVENT_PREUNMOUNT)	| \
+	(1 << DM_EVENT_UNMOUNT)		| \
+	(1 << DM_EVENT_NOSPACE)		| \
+	(1 << DM_EVENT_DEBUT)		| \
+	(1 << DM_EVENT_CREATE)		| \
+	(1 << DM_EVENT_POSTCREATE)	| \
+	(1 << DM_EVENT_REMOVE)		| \
+	(1 << DM_EVENT_POSTREMOVE)	| \
+	(1 << DM_EVENT_RENAME)		| \
+	(1 << DM_EVENT_POSTRENAME)	| \
+	(1 << DM_EVENT_LINK)		| \
+	(1 << DM_EVENT_POSTLINK)	| \
+	(1 << DM_EVENT_SYMLINK)		| \
+	(1 << DM_EVENT_POSTSYMLINK)	| \
+	(1 << DM_EVENT_READ)		| \
+	(1 << DM_EVENT_WRITE)		| \
+	(1 << DM_EVENT_TRUNCATE)	| \
+	(1 << DM_EVENT_ATTRIBUTE)	| \
+	(1 << DM_EVENT_DESTROY)		)
+
+
+/* isolate the read/write/trunc events of a dm_tokevent_t */
+
+#define DM_EVENT_RDWRTRUNC(tevp)			(  \
+	((tevp)->te_msg.ev_type == DM_EVENT_READ)	|| \
+	((tevp)->te_msg.ev_type == DM_EVENT_WRITE)	|| \
+	((tevp)->te_msg.ev_type == DM_EVENT_TRUNCATE)	)
+
+
+/*
+ *  Global handle hack isolation.
+ */
+
+#define DM_GLOBALHAN(hanp, hlen)	(((hanp) == DM_GLOBAL_HANP) && \
+					 ((hlen) == DM_GLOBAL_HLEN))
+
+
+#define DM_MAX_MSG_DATA		3960
+
+
+
+/* Supported filesystem function vector functions. */
+
+
+typedef struct {
+	int				code_level;
+	dm_fsys_clear_inherit_t		clear_inherit;
+	dm_fsys_create_by_handle_t	create_by_handle;
+	dm_fsys_downgrade_right_t	downgrade_right;
+	dm_fsys_get_allocinfo_rvp_t	get_allocinfo_rvp;
+	dm_fsys_get_bulkall_rvp_t	get_bulkall_rvp;
+	dm_fsys_get_bulkattr_rvp_t	get_bulkattr_rvp;
+	dm_fsys_get_config_t		get_config;
+	dm_fsys_get_config_events_t	get_config_events;
+	dm_fsys_get_destroy_dmattr_t	get_destroy_dmattr;
+	dm_fsys_get_dioinfo_t		get_dioinfo;
+	dm_fsys_get_dirattrs_rvp_t	get_dirattrs_rvp;
+	dm_fsys_get_dmattr_t		get_dmattr;
+	dm_fsys_get_eventlist_t		get_eventlist;
+	dm_fsys_get_fileattr_t		get_fileattr;
+	dm_fsys_get_region_t		get_region;
+	dm_fsys_getall_dmattr_t		getall_dmattr;
+	dm_fsys_getall_inherit_t	getall_inherit;
+	dm_fsys_init_attrloc_t		init_attrloc;
+	dm_fsys_mkdir_by_handle_t	mkdir_by_handle;
+	dm_fsys_probe_hole_t		probe_hole;
+	dm_fsys_punch_hole_t		punch_hole;
+	dm_fsys_read_invis_rvp_t	read_invis_rvp;
+	dm_fsys_release_right_t		release_right;
+	dm_fsys_remove_dmattr_t		remove_dmattr;
+	dm_fsys_request_right_t		request_right;
+	dm_fsys_set_dmattr_t		set_dmattr;
+	dm_fsys_set_eventlist_t		set_eventlist;
+	dm_fsys_set_fileattr_t		set_fileattr;
+	dm_fsys_set_inherit_t		set_inherit;
+	dm_fsys_set_region_t		set_region;
+	dm_fsys_symlink_by_handle_t	symlink_by_handle;
+	dm_fsys_sync_by_handle_t	sync_by_handle;
+	dm_fsys_upgrade_right_t		upgrade_right;
+	dm_fsys_write_invis_rvp_t	write_invis_rvp;
+	dm_fsys_obj_ref_hold_t		obj_ref_hold;
+} dm_fsys_vector_t;
+
+
+typedef struct	{
+	struct list_head	ftype_list;	/* list of fstypes */
+	struct list_head	sb_list;	/* list of sb's per fstype */
+	struct file_system_type *f_type;
+	struct filesystem_dmapi_operations *dmapiops;
+	dm_fsys_vector_t	*vptr;
+	struct super_block	*sb;
+} dm_vector_map_t;
+
+
+extern	dm_session_t	*dm_sessions;		/* head of session list */
+extern	dm_fsreg_t	*dm_registers;
+extern	lock_t		dm_reg_lock;		/* lock for registration list */
+
+/*
+ *  Kernel only prototypes.
+ */
+
+int		dm_find_session_and_lock(
+			dm_sessid_t	sid,
+			dm_session_t	**sessionpp,
+			unsigned long	*lcp);
+
+int		dm_find_msg_and_lock(
+			dm_sessid_t	sid,
+			dm_token_t	token,
+			dm_tokevent_t	**tevpp,
+			unsigned long	*lcp);
+
+dm_tokevent_t * dm_evt_create_tevp(
+			dm_eventtype_t	event,
+			int		variable_size,
+			void		**msgpp);
+
+int		dm_app_get_tdp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			short		types,
+			dm_right_t	right,
+			dm_tokdata_t	**tdpp);
+
+int		dm_get_config_tdp(
+			void		__user *hanp,
+			size_t		hlen,
+			dm_tokdata_t	**tdpp);
+
+void		dm_app_put_tdp(
+			dm_tokdata_t	*tdp);
+
+void		dm_put_tevp(
+			dm_tokevent_t	*tevp,
+			dm_tokdata_t	*tdp);
+
+void		dm_evt_rele_tevp(
+			dm_tokevent_t	*tevp,
+			int		droprights);
+
+int		dm_enqueue_normal_event(
+			struct super_block *sbp,
+			dm_tokevent_t	**tevpp,
+			int		flags);
+
+int		dm_enqueue_mount_event(
+			struct super_block *sbp,
+			dm_tokevent_t	*tevp);
+
+int		dm_enqueue_sendmsg_event(
+			dm_sessid_t	targetsid,
+			dm_tokevent_t	*tevp,
+			int		synch);
+
+int		dm_enqueue_user_event(
+			dm_sessid_t	sid,
+			dm_tokevent_t	*tevp,
+			dm_token_t	*tokenp);
+
+int		dm_obj_ref_query_rvp(
+			dm_sessid_t	sid,
+			dm_token_t	token,
+			void		__user *hanp,
+			size_t		hlen,
+			int		*rvp);
+
+int		dm_read_invis_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_off_t	off,
+			dm_size_t	len,
+			void		__user *bufp,
+			int		*rvp);
+
+int		dm_write_invis_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			int		flags,
+			dm_off_t	off,
+			dm_size_t	len,
+			void		__user *bufp,
+			int		*rvp);
+
+int		dm_get_bulkattr_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			u_int		mask,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp,
+			int		*rvp);
+
+int		dm_get_bulkall_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			u_int		mask,
+			dm_attrname_t	__user *attrnamep,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp,
+			int		*rvp);
+
+int		dm_get_dirattrs_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			u_int		mask,
+			dm_attrloc_t	__user *locp,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp,
+			int		*rvp);
+
+int		dm_get_allocinfo_rvp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_off_t	__user	*offp,
+			u_int		nelem,
+			dm_extent_t	__user *extentp,
+			u_int		__user *nelemp,
+			int		*rvp);
+
+int		dm_waitfor_destroy_attrname(
+			struct super_block	*sb,
+			dm_attrname_t	*attrnamep);
+
+void		dm_clear_fsreg(
+			dm_session_t	*s);
+
+int		dm_add_fsys_entry(
+			struct super_block	*sb,
+			dm_tokevent_t	*tevp);
+
+void		dm_change_fsys_entry(
+			struct super_block	*sb,
+			dm_fsstate_t	newstate);
+
+void		dm_remove_fsys_entry(
+			struct super_block	*sb);
+
+dm_fsys_vector_t *dm_fsys_vector(
+			struct inode	*ip);
+
+struct filesystem_dmapi_operations *dm_fsys_ops(
+			struct super_block	*sb);
+
+void dm_fsys_ops_release(
+			 struct super_block	*sb);
+
+int		dm_waitfor_disp_session(
+			struct super_block	*sb,
+			dm_tokevent_t	*tevp,
+			dm_session_t	**sessionpp,
+			unsigned long	*lcp);
+
+struct inode *	dm_handle_to_ip (
+			dm_handle_t	*handlep,
+			short		*typep);
+
+int		dm_check_dmapi_ip(
+			struct inode	*ip);
+
+dm_tokevent_t * dm_find_mount_tevp_and_lock(
+			dm_fsid_t	*fsidp,
+			unsigned long	*lcp);
+
+int		dm_path_to_hdl(
+			char		__user *path,
+			void		__user *hanp,
+			size_t		__user *hlenp);
+
+int		dm_path_to_fshdl(
+			char		__user *path,
+			void		__user *hanp,
+			size_t		__user *hlenp);
+
+int		dm_fd_to_hdl(
+			int		fd,
+			void		__user *hanp,
+			size_t		__user *hlenp);
+
+int		dm_upgrade_right(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token);
+
+int		dm_downgrade_right(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token);
+
+int		dm_request_right(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			u_int		flags,
+			dm_right_t	right);
+
+int		dm_release_right(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token);
+
+int		dm_query_right(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_right_t	__user *rightp);
+
+
+int		dm_set_eventlist(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_eventset_t	__user *eventsetp,
+			u_int		maxevent);
+
+int		dm_obj_ref_hold(
+			dm_sessid_t	sid,
+			dm_token_t	token,
+			void		__user *hanp,
+			size_t		hlen);
+
+int		dm_obj_ref_rele(
+			dm_sessid_t	sid,
+			dm_token_t	token,
+			void		__user *hanp,
+			size_t		hlen);
+
+int		dm_get_eventlist(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			u_int		nelem,
+			dm_eventset_t	__user *eventsetp,
+			u_int		__user *nelemp);
+
+
+int		dm_set_disp(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_eventset_t	__user *eventsetp,
+			u_int		maxevent);
+
+
+int		dm_set_return_on_destroy(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			dm_attrname_t	__user *attrnamep,
+			dm_boolean_t	enable);
+
+
+int		dm_get_mountinfo(
+			dm_sessid_t	sid,
+			void		__user *hanp,
+			size_t		hlen,
+			dm_token_t	token,
+			size_t		buflen,
+			void		__user *bufp,
+			size_t		__user *rlenp);
+
+void		dm_link_event(
+			dm_tokevent_t	*tevp,
+			dm_eventq_t	*queue);
+
+void		dm_unlink_event(
+			dm_tokevent_t	*tevp,
+			dm_eventq_t	*queue);
+
+int		dm_open_by_handle_rvp(
+			unsigned int	fd,
+			void		__user *hanp,
+			size_t		hlen,
+			int		mode,
+			int		*rvp);
+
+int		dm_copyin_handle(
+			void		__user *hanp,
+			size_t		hlen,
+			dm_handle_t	*handlep);
+
+int		dm_release_disp_threads(
+			dm_fsid_t	*fsid,
+			struct inode	*inode,
+			int		errno);
+
+#endif	/* _DMAPI_PRIVATE_H */
--- /dev/null
+++ b/fs/dmapi/dmapi_region.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+int
+dm_get_region(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		nelem,
+	dm_region_t	__user *regbufp,
+	u_int		__user *nelemp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_region(tdp->td_ip, tdp->td_right,
+		nelem, regbufp, nelemp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+
+int
+dm_set_region(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		nelem,
+	dm_region_t	__user *regbufp,
+	dm_boolean_t	__user *exactflagp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_REG,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->set_region(tdp->td_ip, tdp->td_right,
+		nelem, regbufp, exactflagp);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_register.c
@@ -0,0 +1,1638 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/version.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <asm/uaccess.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+/* LOOKUP_POSTIVE was removed in Linux 2.6 */
+#ifndef LOOKUP_POSITIVE
+#define LOOKUP_POSITIVE	0
+#endif
+
+dm_fsreg_t	*dm_registers;	/* head of filesystem registration list */
+int		dm_fsys_cnt;	/* number of filesystems on dm_registers list */
+lock_t		dm_reg_lock = SPIN_LOCK_UNLOCKED;/* lock for dm_registers */
+
+
+
+#ifdef CONFIG_PROC_FS
+static int
+fsreg_read_pfs(char *buffer, char **start, off_t offset,
+		 int count, int *eof, void *data)
+{
+	int len;
+	int i;
+	dm_fsreg_t	*fsrp = (dm_fsreg_t*)data;
+	char		statebuf[30];
+
+#define CHKFULL if(len >= count) break;
+#define ADDBUF(a,b)	len += sprintf(buffer + len, a, b); CHKFULL;
+
+	switch (fsrp->fr_state) {
+	case DM_STATE_MOUNTING:		sprintf(statebuf, "mounting"); break;
+	case DM_STATE_MOUNTED:		sprintf(statebuf, "mounted"); break;
+	case DM_STATE_UNMOUNTING:	sprintf(statebuf, "unmounting"); break;
+	case DM_STATE_UNMOUNTED:	sprintf(statebuf, "unmounted"); break;
+	default:
+		sprintf(statebuf, "unknown:%d", (int)fsrp->fr_state);
+		break;
+	}
+
+	len=0;
+	while(1){
+		ADDBUF("fsrp=0x%p\n", fsrp);
+		ADDBUF("fr_next=0x%p\n", fsrp->fr_next);
+		ADDBUF("fr_sb=0x%p\n", fsrp->fr_sb);
+		ADDBUF("fr_tevp=0x%p\n", fsrp->fr_tevp);
+		ADDBUF("fr_fsid=%c\n", '?');
+		ADDBUF("fr_msg=0x%p\n", fsrp->fr_msg);
+		ADDBUF("fr_msgsize=%d\n", fsrp->fr_msgsize);
+		ADDBUF("fr_state=%s\n", statebuf);
+		ADDBUF("fr_dispq=%c\n", '?');
+		ADDBUF("fr_dispcnt=%d\n", fsrp->fr_dispcnt);
+
+		ADDBUF("fr_evt_dispq.eq_head=0x%p\n", fsrp->fr_evt_dispq.eq_head);
+		ADDBUF("fr_evt_dispq.eq_tail=0x%p\n", fsrp->fr_evt_dispq.eq_tail);
+		ADDBUF("fr_evt_dispq.eq_count=%d\n", fsrp->fr_evt_dispq.eq_count);
+
+		ADDBUF("fr_queue=%c\n", '?');
+		ADDBUF("fr_lock=%c\n", '?');
+		ADDBUF("fr_hdlcnt=%d\n", fsrp->fr_hdlcnt);
+		ADDBUF("fr_vfscnt=%d\n", fsrp->fr_vfscnt);
+		ADDBUF("fr_unmount=%d\n", fsrp->fr_unmount);
+
+		len += sprintf(buffer + len, "fr_rattr=");
+		CHKFULL;
+		for(i = 0; i <= DM_ATTR_NAME_SIZE; ++i){
+			ADDBUF("%c", fsrp->fr_rattr.an_chars[i]);
+		}
+		CHKFULL;
+		len += sprintf(buffer + len, "\n");
+		CHKFULL;
+
+		for(i = 0; i < DM_EVENT_MAX; i++){
+			if( fsrp->fr_sessp[i] != NULL ){
+				ADDBUF("fr_sessp[%d]=", i);
+				ADDBUF("0x%p\n", fsrp->fr_sessp[i]);
+			}
+		}
+		CHKFULL;
+
+		break;
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		*eof = 1;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
+
+	return len;
+}
+#endif
+
+
+/* Returns a pointer to the filesystem structure for the filesystem
+   referenced by fsidp. The caller is responsible for obtaining dm_reg_lock
+   before calling this routine.
+*/
+
+static dm_fsreg_t *
+dm_find_fsreg(
+	dm_fsid_t	*fsidp)
+{
+	dm_fsreg_t	*fsrp;
+
+	for (fsrp = dm_registers; fsrp; fsrp = fsrp->fr_next) {
+		if (!memcmp(&fsrp->fr_fsid, fsidp, sizeof(*fsidp)))
+			break;
+	}
+	return(fsrp);
+}
+
+
+/* Given a fsid_t, dm_find_fsreg_and_lock() finds the dm_fsreg_t structure
+   for that filesytem if one exists, and returns a pointer to the structure
+   after obtaining its 'fr_lock' so that the caller can safely modify the
+   dm_fsreg_t.	The caller is responsible for releasing 'fr_lock'.
+*/
+
+static dm_fsreg_t *
+dm_find_fsreg_and_lock(
+	dm_fsid_t	*fsidp,
+	unsigned long	*lcp)		/* address of returned lock cookie */
+{
+	dm_fsreg_t	*fsrp;
+
+	for (;;) {
+		*lcp = mutex_spinlock(&dm_reg_lock);
+
+		if ((fsrp = dm_find_fsreg(fsidp)) == NULL) {
+			mutex_spinunlock(&dm_reg_lock, *lcp);
+			return(NULL);
+		}
+		if (spin_trylock(&fsrp->fr_lock)) {
+			nested_spinunlock(&dm_reg_lock);
+			return(fsrp);	/* success */
+		}
+
+		/* If the second lock is not available, drop the first and
+		   start over.	This gives the CPU a chance to process any
+		   interrupts, and also allows processes which want a fr_lock
+		   for a different filesystem to proceed.
+		*/
+
+		mutex_spinunlock(&dm_reg_lock, *lcp);
+	}
+}
+
+
+/* dm_add_fsys_entry() is called when a DM_EVENT_MOUNT event is about to be
+   sent.  It creates a dm_fsreg_t structure for the filesystem and stores a
+   pointer to a copy of the mount event within that structure so that it is
+   available for subsequent dm_get_mountinfo() calls.
+*/
+
+int
+dm_add_fsys_entry(
+	struct super_block *sb,
+	dm_tokevent_t	*tevp)
+{
+	dm_fsreg_t	*fsrp;
+	int		msgsize;
+	void		*msg;
+	unsigned long	lc;			/* lock cookie */
+	dm_fsid_t	fsid;
+	struct filesystem_dmapi_operations *dops;
+
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+	dops->get_fsid(sb, &fsid);
+
+	/* Allocate and initialize a dm_fsreg_t structure for the filesystem. */
+
+	msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_event);
+	msg = kmalloc(msgsize, GFP_KERNEL);
+	if (msg == NULL) {
+		printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+		return -ENOMEM;
+	}
+	memcpy(msg, &tevp->te_event, msgsize);
+
+	fsrp = kmem_cache_alloc(dm_fsreg_cachep, GFP_KERNEL);
+	if (fsrp == NULL) {
+		kfree(msg);
+		printk("%s/%d: kmem_cache_alloc(dm_fsreg_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+		return -ENOMEM;
+	}
+	memset(fsrp, 0, sizeof(*fsrp));
+
+	fsrp->fr_sb = sb;
+	fsrp->fr_tevp = tevp;
+	memcpy(&fsrp->fr_fsid, &fsid, sizeof(fsid));
+	fsrp->fr_msg = msg;
+	fsrp->fr_msgsize = msgsize;
+	fsrp->fr_state = DM_STATE_MOUNTING;
+	sv_init(&fsrp->fr_dispq, SV_DEFAULT, "fr_dispq");
+	sv_init(&fsrp->fr_queue, SV_DEFAULT, "fr_queue");
+	spinlock_init(&fsrp->fr_lock, "fr_lock");
+
+	/* If no other mounted DMAPI filesystem already has this same
+	   fsid_t, then add this filesystem to the list.
+	*/
+
+	lc = mutex_spinlock(&dm_reg_lock);
+
+	if (!dm_find_fsreg(&fsid)) {
+		fsrp->fr_next = dm_registers;
+		dm_registers = fsrp;
+		dm_fsys_cnt++;
+		mutex_spinunlock(&dm_reg_lock, lc);
+#ifdef CONFIG_PROC_FS
+		{
+		char buf[100];
+		struct proc_dir_entry *entry;
+
+		sprintf(buf, DMAPI_DBG_PROCFS "/fsreg/0x%p", fsrp);
+		entry = create_proc_read_entry(buf, 0, NULL, fsreg_read_pfs, fsrp);
+		}
+#endif
+		return(0);
+	}
+
+	/* A fsid_t collision occurred, so prevent this new filesystem from
+	   mounting.
+	*/
+
+	mutex_spinunlock(&dm_reg_lock, lc);
+
+	sv_destroy(&fsrp->fr_dispq);
+	sv_destroy(&fsrp->fr_queue);
+	spinlock_destroy(&fsrp->fr_lock);
+	kfree(msg);
+	kmem_cache_free(dm_fsreg_cachep, fsrp);
+	return(-EBUSY);
+}
+
+
+/* dm_change_fsys_entry() is called whenever a filesystem's mount state is
+   about to change.  The state is changed to DM_STATE_MOUNTED after a
+   successful DM_EVENT_MOUNT event or after a failed unmount.  It is changed
+   to DM_STATE_UNMOUNTING after a successful DM_EVENT_PREUNMOUNT event.
+   Finally, the state is changed to DM_STATE_UNMOUNTED after a successful
+   unmount.  It stays in this state until the DM_EVENT_UNMOUNT event is
+   queued, at which point the filesystem entry is removed.
+*/
+
+void
+dm_change_fsys_entry(
+	struct super_block *sb,
+	dm_fsstate_t	newstate)
+{
+	dm_fsreg_t	*fsrp;
+	int		seq_error;
+	unsigned long	lc;			/* lock cookie */
+	dm_fsid_t	fsid;
+	struct filesystem_dmapi_operations *dops;
+
+	/* Find the filesystem referenced by the sb's fsid_t.	 This should
+	   always succeed.
+	*/
+
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+	dops->get_fsid(sb, &fsid);
+
+	if ((fsrp = dm_find_fsreg_and_lock(&fsid, &lc)) == NULL) {
+		panic("dm_change_fsys_entry: can't find DMAPI fsrp for "
+			"sb %p\n", sb);
+	}
+
+	/* Make sure that the new state is acceptable given the current state
+	   of the filesystem.  Any error here is a major DMAPI/filesystem
+	   screwup.
+	*/
+
+	seq_error = 0;
+	switch (newstate) {
+	case DM_STATE_MOUNTED:
+		if (fsrp->fr_state != DM_STATE_MOUNTING &&
+		    fsrp->fr_state != DM_STATE_UNMOUNTING) {
+			seq_error++;
+		}
+		break;
+	case DM_STATE_UNMOUNTING:
+		if (fsrp->fr_state != DM_STATE_MOUNTED)
+			seq_error++;
+		break;
+	case DM_STATE_UNMOUNTED:
+		if (fsrp->fr_state != DM_STATE_UNMOUNTING)
+			seq_error++;
+		break;
+	default:
+		seq_error++;
+		break;
+	}
+	if (seq_error) {
+		panic("dm_change_fsys_entry: DMAPI sequence error: old state "
+			"%d, new state %d, fsrp %p\n", fsrp->fr_state,
+			newstate, fsrp);
+	}
+
+	/* If the old state was DM_STATE_UNMOUNTING, then processes could be
+	   sleeping in dm_handle_to_ip() waiting for their DM_NO_TOKEN handles
+	   to be translated to inodes.	Wake them up so that they either
+	   continue (new state is DM_STATE_MOUNTED) or fail (new state is
+	   DM_STATE_UNMOUNTED).
+	*/
+
+	if (fsrp->fr_state == DM_STATE_UNMOUNTING) {
+		if (fsrp->fr_hdlcnt)
+			sv_broadcast(&fsrp->fr_queue);
+	}
+
+	/* Change the filesystem's mount state to its new value. */
+
+	fsrp->fr_state = newstate;
+	fsrp->fr_tevp = NULL;		/* not valid after DM_STATE_MOUNTING */
+
+	/* If the new state is DM_STATE_UNMOUNTING, wait until any application
+	   threads currently in the process of making VFS_VGET and VFS_ROOT
+	   calls are done before we let this unmount thread continue the
+	   unmount.  (We want to make sure that the unmount will see these
+	   inode references during its scan.)
+	*/
+
+	if (newstate == DM_STATE_UNMOUNTING) {
+		while (fsrp->fr_vfscnt) {
+			fsrp->fr_unmount++;
+			sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc);
+			lc = mutex_spinlock(&fsrp->fr_lock);
+			fsrp->fr_unmount--;
+		}
+	}
+
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+}
+
+
+/* dm_remove_fsys_entry() gets called after a failed mount or after an
+   DM_EVENT_UNMOUNT event has been queued.  (The filesystem entry must stay
+   until the DM_EVENT_UNMOUNT reply is queued so that the event can use the
+   'fr_sessp' list to see which session to send the event to.)
+*/
+
+void
+dm_remove_fsys_entry(
+	struct super_block *sb)
+{
+	dm_fsreg_t	**fsrpp;
+	dm_fsreg_t	*fsrp;
+	unsigned long	lc;			/* lock cookie */
+	struct filesystem_dmapi_operations *dops;
+	dm_fsid_t	fsid;
+
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+	dops->get_fsid(sb, &fsid);
+
+	/* Find the filesystem referenced by the sb's fsid_t and dequeue
+	   it after verifying that the fr_state shows a filesystem that is
+	   either mounting or unmounted.
+	*/
+
+	lc = mutex_spinlock(&dm_reg_lock);
+
+	fsrpp = &dm_registers;
+	while ((fsrp = *fsrpp) != NULL) {
+		if (!memcmp(&fsrp->fr_fsid, &fsid, sizeof(fsrp->fr_fsid)))
+			break;
+		fsrpp = &fsrp->fr_next;
+	}
+	if (fsrp == NULL) {
+		mutex_spinunlock(&dm_reg_lock, lc);
+		panic("dm_remove_fsys_entry: can't find DMAPI fsrp for "
+			"sb %p\n", sb);
+	}
+
+	nested_spinlock(&fsrp->fr_lock);
+
+	/* Verify that it makes sense to remove this entry. */
+
+	if (fsrp->fr_state != DM_STATE_MOUNTING &&
+	    fsrp->fr_state != DM_STATE_UNMOUNTED) {
+		nested_spinunlock(&fsrp->fr_lock);
+		mutex_spinunlock(&dm_reg_lock, lc);
+		panic("dm_remove_fsys_entry: DMAPI sequence error: old state "
+			"%d, fsrp %p\n", fsrp->fr_state, fsrp);
+	}
+
+	*fsrpp = fsrp->fr_next;
+	dm_fsys_cnt--;
+
+	nested_spinunlock(&dm_reg_lock);
+
+	/* Since the filesystem is about to finish unmounting, we must be sure
+	   that no inodes are being referenced within the filesystem before we
+	   let this event thread continue.  If the filesystem is currently in
+	   state DM_STATE_MOUNTING, then we know by definition that there can't
+	   be any references.  If the filesystem is DM_STATE_UNMOUNTED, then
+	   any application threads referencing handles with DM_NO_TOKEN should
+	   have already been awakened by dm_change_fsys_entry and should be
+	   long gone by now.  Just in case they haven't yet left, sleep here
+	   until they are really gone.
+	*/
+
+	while (fsrp->fr_hdlcnt) {
+		fsrp->fr_unmount++;
+		sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc);
+		lc = mutex_spinlock(&fsrp->fr_lock);
+		fsrp->fr_unmount--;
+	}
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	/* Release all memory. */
+
+#ifdef CONFIG_PROC_FS
+	{
+	char buf[100];
+	sprintf(buf, DMAPI_DBG_PROCFS "/fsreg/0x%p", fsrp);
+	remove_proc_entry(buf, NULL);
+	}
+#endif
+	dm_fsys_ops_release(sb);
+	sv_destroy(&fsrp->fr_dispq);
+	sv_destroy(&fsrp->fr_queue);
+	spinlock_destroy(&fsrp->fr_lock);
+	kfree(fsrp->fr_msg);
+	kmem_cache_free(dm_fsreg_cachep, fsrp);
+}
+
+
+/* Get an inode for the object referenced by handlep.  We cannot use
+   altgetvfs() because it fails if the VFS_OFFLINE bit is set, which means
+   that any call to dm_handle_to_ip() while a umount is in progress would
+   return an error, even if the umount can't possibly succeed because users
+   are in the filesystem.  The requests would start to fail as soon as the
+   umount begins, even before the application receives the DM_EVENT_PREUNMOUNT
+   event.
+
+   dm_handle_to_ip() emulates the behavior of lookup() while an unmount is
+   in progress.	 Any call to dm_handle_to_ip() while the filesystem is in the
+   DM_STATE_UNMOUNTING state will block.  If the unmount eventually succeeds,
+   the requests will wake up and fail.	If the unmount fails, the requests will
+   wake up and complete normally.
+
+   While a filesystem is in state DM_STATE_MOUNTING, dm_handle_to_ip() will
+   fail all requests.  Per the DMAPI spec, the only handles in the filesystem
+   which are valid during a mount event are the handles within the event
+   itself.
+*/
+
+struct inode *
+dm_handle_to_ip(
+	dm_handle_t	*handlep,
+	short		*typep)
+{
+	dm_fsreg_t	*fsrp;
+	short		type;
+	unsigned long	lc;			/* lock cookie */
+	int		error = 0;
+	dm_fid_t	*fidp;
+	struct super_block *sb;
+	struct inode	*ip;
+	int		filetype;
+	struct filesystem_dmapi_operations *dmapiops;
+
+	if ((fsrp = dm_find_fsreg_and_lock(&handlep->ha_fsid, &lc)) == NULL)
+		return NULL;
+
+	fidp = (dm_fid_t*)&handlep->ha_fid;
+	/* If mounting, and we are not asking for a filesystem handle,
+	 * then fail the request.  (dm_fid_len==0 for fshandle)
+	 */
+	if ((fsrp->fr_state == DM_STATE_MOUNTING) &&
+	    (fidp->dm_fid_len != 0)) {
+		mutex_spinunlock(&fsrp->fr_lock, lc);
+		return NULL;
+	}
+
+	for (;;) {
+		if (fsrp->fr_state == DM_STATE_MOUNTING)
+			break;
+		if (fsrp->fr_state == DM_STATE_MOUNTED)
+			break;
+		if (fsrp->fr_state == DM_STATE_UNMOUNTED) {
+			if (fsrp->fr_unmount && fsrp->fr_hdlcnt == 0)
+				sv_broadcast(&fsrp->fr_queue);
+			mutex_spinunlock(&fsrp->fr_lock, lc);
+			return NULL;
+		}
+
+		/* Must be DM_STATE_UNMOUNTING. */
+
+		fsrp->fr_hdlcnt++;
+		sv_wait(&fsrp->fr_queue, 1, &fsrp->fr_lock, lc);
+		lc = mutex_spinlock(&fsrp->fr_lock);
+		fsrp->fr_hdlcnt--;
+	}
+
+	fsrp->fr_vfscnt++;
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	/* Now that the mutex is released, wait until we have access to the
+	   inode.
+	*/
+
+	sb = fsrp->fr_sb;
+	error = -ENOSYS;
+	dmapiops = dm_fsys_ops(sb);
+	ASSERT(dmapiops);
+	if (dmapiops->fh_to_inode)
+		error = dmapiops->fh_to_inode(sb, &ip, (void*)fidp);
+
+	lc = mutex_spinlock(&fsrp->fr_lock);
+
+	fsrp->fr_vfscnt--;
+	if (fsrp->fr_unmount && fsrp->fr_vfscnt == 0)
+		sv_broadcast(&fsrp->fr_queue);
+
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+	if (error || ip == NULL)
+		return NULL;
+
+	filetype = ip->i_mode & S_IFMT;
+	if (fidp->dm_fid_len == 0) {
+		type = DM_TDT_VFS;
+	} else if (filetype == S_IFREG) {
+		type = DM_TDT_REG;
+	} else if (filetype == S_IFDIR) {
+		type = DM_TDT_DIR;
+	} else if (filetype == S_IFLNK) {
+		type = DM_TDT_LNK;
+	} else {
+		type = DM_TDT_OTH;
+	}
+	*typep = type;
+	return ip;
+}
+
+
+int
+dm_ip_to_handle(
+	struct inode	*ip,
+	dm_handle_t	*handlep)
+{
+	int		error;
+	dm_fid_t	fid;
+	dm_fsid_t	fsid;
+	int		hsize;
+	struct filesystem_dmapi_operations *dops;
+
+	dops = dm_fsys_ops(ip->i_sb);
+	ASSERT(dops);
+
+	error = dops->inode_to_fh(ip, &fid, &fsid);
+	if (error)
+		return error;
+
+	memcpy(&handlep->ha_fsid, &fsid, sizeof(fsid));
+	memcpy(&handlep->ha_fid, &fid, fid.dm_fid_len + sizeof fid.dm_fid_len);
+	hsize = DM_HSIZE(*handlep);
+	memset((char *)handlep + hsize, 0, sizeof(*handlep) - hsize);
+	return 0;
+}
+
+
+/* Given an inode, check if that inode resides in filesystem that supports
+   DMAPI.  Returns zero if the inode is in a DMAPI filesystem, otherwise
+   returns an errno.
+*/
+
+int
+dm_check_dmapi_ip(
+	struct inode	*ip)
+{
+	dm_handle_t	handle;
+	/* REFERENCED */
+	dm_fsreg_t	*fsrp;
+	int		error;
+	unsigned long	lc;			/* lock cookie */
+
+	if ((error = dm_ip_to_handle(ip, &handle)) != 0)
+		return(error);
+
+	if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL)
+		return(-EBADF);
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+	return(0);
+}
+
+
+/* Return a pointer to the DM_EVENT_MOUNT event while a mount is still in
+   progress.  This is only called by dm_get_config and dm_get_config_events
+   which need to access the filesystem during a mount but which don't have
+   a session and token to use.
+*/
+
+dm_tokevent_t *
+dm_find_mount_tevp_and_lock(
+	dm_fsid_t	*fsidp,
+	unsigned long	*lcp)		/* address of returned lock cookie */
+{
+	dm_fsreg_t	*fsrp;
+
+	if ((fsrp = dm_find_fsreg_and_lock(fsidp, lcp)) == NULL)
+		return(NULL);
+
+	if (!fsrp->fr_tevp || fsrp->fr_state != DM_STATE_MOUNTING) {
+		mutex_spinunlock(&fsrp->fr_lock, *lcp);
+		return(NULL);
+	}
+	nested_spinlock(&fsrp->fr_tevp->te_lock);
+	nested_spinunlock(&fsrp->fr_lock);
+	return(fsrp->fr_tevp);
+}
+
+
+/* Wait interruptibly until a session registers disposition for 'event' in
+   filesystem 'sb'.  Upon successful exit, both the filesystem's dm_fsreg_t
+   structure and the session's dm_session_t structure are locked.  The caller
+   is responsible for unlocking both structures using the returned cookies.
+
+   Warning: The locks can be dropped in any order, but the 'lc2p' cookie MUST
+   BE USED FOR THE FIRST UNLOCK, and the lc1p cookie must be used for the
+   second unlock.  If this is not done, the CPU will be interruptible while
+   holding a mutex, which could deadlock the machine!
+*/
+
+static int
+dm_waitfor_disp(
+	struct super_block *sb,
+	dm_tokevent_t	*tevp,
+	dm_fsreg_t	**fsrpp,
+	unsigned long	*lc1p,		/* addr of first returned lock cookie */
+	dm_session_t	**sessionpp,
+	unsigned long	*lc2p)		/* addr of 2nd returned lock cookie */
+{
+	dm_eventtype_t	event = tevp->te_msg.ev_type;
+	dm_session_t	*s;
+	dm_fsreg_t	*fsrp;
+	dm_fsid_t	fsid;
+	struct filesystem_dmapi_operations *dops;
+
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+
+	dops->get_fsid(sb, &fsid);
+	if ((fsrp = dm_find_fsreg_and_lock(&fsid, lc1p)) == NULL)
+		return -ENOENT;
+
+	/* If no session is registered for this event in the specified
+	   filesystem, then sleep interruptibly until one does.
+	*/
+
+	for (;;) {
+		int	rc = 0;
+
+		/* The dm_find_session_and_lock() call is needed because a
+		   session that is in the process of being removed might still
+		   be in the dm_fsreg_t structure but won't be in the
+		   dm_sessions list.
+		*/
+
+		if ((s = fsrp->fr_sessp[event]) != NULL &&
+		    dm_find_session_and_lock(s->sn_sessid, &s, lc2p) == 0) {
+			break;
+		}
+
+		/* Noone is currently registered.  DM_EVENT_UNMOUNT events
+		   don't wait for anyone to register because the unmount is
+		   already past the point of no return.
+		*/
+
+		if (event == DM_EVENT_UNMOUNT) {
+			mutex_spinunlock(&fsrp->fr_lock, *lc1p);
+			return -ENOENT;
+		}
+
+		/* Wait until a session registers for disposition of this
+		   event.
+		*/
+
+		fsrp->fr_dispcnt++;
+		dm_link_event(tevp, &fsrp->fr_evt_dispq);
+
+		sv_wait_sig(&fsrp->fr_dispq, 1, &fsrp->fr_lock, *lc1p);
+		rc = signal_pending(current);
+
+		*lc1p = mutex_spinlock(&fsrp->fr_lock);
+		fsrp->fr_dispcnt--;
+		dm_unlink_event(tevp, &fsrp->fr_evt_dispq);
+#ifdef HAVE_DM_QUEUE_FLUSH
+		if (tevp->te_flags & DM_TEF_FLUSH) {
+			mutex_spinunlock(&fsrp->fr_lock, *lc1p);
+			return tevp->te_reply;
+		}
+#endif /* HAVE_DM_QUEUE_FLUSH */
+		if (rc) {		/* if signal was received */
+			mutex_spinunlock(&fsrp->fr_lock, *lc1p);
+			return -EINTR;
+		}
+	}
+	*sessionpp = s;
+	*fsrpp = fsrp;
+	return 0;
+}
+
+
+/* Returns the session pointer for the session registered for an event
+   in the given sb.  If successful, the session is locked upon return.  The
+   caller is responsible for releasing the lock.  If no session is currently
+   registered for the event, dm_waitfor_disp_session() will sleep interruptibly
+   until a registration occurs.
+*/
+
+int
+dm_waitfor_disp_session(
+	struct super_block *sb,
+	dm_tokevent_t	*tevp,
+	dm_session_t	**sessionpp,
+	unsigned long	*lcp)
+{
+	dm_fsreg_t	*fsrp;
+	unsigned long	lc2;
+	int		error;
+
+	if (tevp->te_msg.ev_type < 0 || tevp->te_msg.ev_type > DM_EVENT_MAX)
+		return(-EIO);
+
+	error = dm_waitfor_disp(sb, tevp, &fsrp, lcp, sessionpp, &lc2);
+	if (!error)
+		mutex_spinunlock(&fsrp->fr_lock, lc2);	/* rev. cookie order*/
+	return(error);
+}
+
+
+/* Find the session registered for the DM_EVENT_DESTROY event on the specified
+   filesystem, sleeping if necessary until registration occurs.	 Once found,
+   copy the session's return-on-destroy attribute name, if any, back to the
+   caller.
+*/
+
+int
+dm_waitfor_destroy_attrname(
+	struct super_block *sbp,
+	dm_attrname_t	*attrnamep)
+{
+	dm_tokevent_t	*tevp;
+	dm_session_t	*s;
+	dm_fsreg_t	*fsrp;
+	int		error;
+	unsigned long	lc1;		/* first lock cookie */
+	unsigned long	lc2;		/* second lock cookie */
+	void		*msgp;
+
+	tevp = dm_evt_create_tevp(DM_EVENT_DESTROY, 1, (void**)&msgp);
+	error = dm_waitfor_disp(sbp, tevp, &fsrp, &lc1, &s, &lc2);
+	if (!error) {
+		*attrnamep = fsrp->fr_rattr;		/* attribute or zeros */
+		mutex_spinunlock(&s->sn_qlock, lc2);	/* rev. cookie order */
+		mutex_spinunlock(&fsrp->fr_lock, lc1);
+	}
+	dm_evt_rele_tevp(tevp,0);
+	return(error);
+}
+
+
+/* Unregisters the session for the disposition of all events on all
+   filesystems.	 This routine is not called until the session has been
+   dequeued from the session list and its session lock has been dropped,
+   but before the actual structure is freed, so it is safe to grab the
+   'dm_reg_lock' here.	If dm_waitfor_disp_session() happens to be called
+   by another thread, it won't find this session on the session list and
+   will wait until a new session registers.
+*/
+
+void
+dm_clear_fsreg(
+	dm_session_t	*s)
+{
+	dm_fsreg_t	*fsrp;
+	int		event;
+	unsigned long	lc;			/* lock cookie */
+
+	lc = mutex_spinlock(&dm_reg_lock);
+
+	for (fsrp = dm_registers; fsrp != NULL; fsrp = fsrp->fr_next) {
+		nested_spinlock(&fsrp->fr_lock);
+		for (event = 0; event < DM_EVENT_MAX; event++) {
+			if (fsrp->fr_sessp[event] != s)
+				continue;
+			fsrp->fr_sessp[event] = NULL;
+			if (event == DM_EVENT_DESTROY)
+				memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr));
+		}
+		nested_spinunlock(&fsrp->fr_lock);
+	}
+
+	mutex_spinunlock(&dm_reg_lock, lc);
+}
+
+
+/*
+ *  Return the handle for the object named by path.
+ */
+
+int
+dm_path_to_hdl(
+	char		__user *path,	/* any path name */
+	void		__user *hanp,	/* user's data buffer */
+	size_t		__user *hlenp)	/* set to size of data copied */
+{
+	/* REFERENCED */
+	dm_fsreg_t	*fsrp;
+	dm_handle_t	handle;
+	size_t		hlen;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+	struct nameidata nd;
+	struct inode *inode;
+	size_t		len;
+	char		*name;
+	struct filesystem_dmapi_operations *dops;
+
+	/* XXX get things straightened out so getname() works here? */
+	if (!(len = strnlen_user(path, PATH_MAX)))
+		return(-EFAULT);
+	if (len == 1)
+		return(-ENOENT);
+	if (len > PATH_MAX)
+		return(-ENAMETOOLONG);
+	name = kmalloc(len, GFP_KERNEL);
+	if (name == NULL) {
+		printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+		return(-ENOMEM);
+	}
+	if (copy_from_user(name, path, len)) {
+		kfree(name);
+		return(-EFAULT);
+	}
+
+	error = path_lookup(name, LOOKUP_POSITIVE, &nd);
+	kfree(name);
+	if (error)
+		return error;
+
+	ASSERT(nd.path.dentry);
+	ASSERT(nd.path.dentry->d_inode);
+	inode = igrab(nd.path.dentry->d_inode);
+	path_put(&nd.path);
+
+	dops = dm_fsys_ops(inode->i_sb);
+	if (dops == NULL) {
+		/* No longer in a dmapi-capable filesystem...Toto */
+		iput(inode);
+		return -EINVAL;
+	}
+
+	/* we need the inode */
+	error = dm_ip_to_handle(inode, &handle);
+	iput(inode);
+	if (error)
+		return(error);
+
+	if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL)
+		return(-EBADF);
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	hlen = DM_HSIZE(handle);
+
+	if (copy_to_user(hanp, &handle, (int)hlen))
+		return(-EFAULT);
+	if (put_user(hlen,hlenp))
+		return(-EFAULT);
+	return 0;
+}
+
+
+/*
+ *  Return the handle for the file system containing the object named by path.
+ */
+
+int
+dm_path_to_fshdl(
+	char		__user *path,	/* any path name */
+	void		__user *hanp,	/* user's data buffer */
+	size_t		__user *hlenp)	/* set to size of data copied */
+{
+	/* REFERENCED */
+	dm_fsreg_t	*fsrp;
+	dm_handle_t	handle;
+	size_t		hlen;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+	struct nameidata nd;
+	struct inode *inode;
+	size_t		len;
+	char		*name;
+	struct filesystem_dmapi_operations *dops;
+
+	/* XXX get things straightened out so getname() works here? */
+	if(!(len = strnlen_user(path, PATH_MAX)))
+		return(-EFAULT);
+	if (len == 1)
+		return(-ENOENT);
+	if (len > PATH_MAX)
+		return(-ENAMETOOLONG);
+	name = kmalloc(len, GFP_KERNEL);
+	if (name == NULL) {
+		printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+		return(-ENOMEM);
+	}
+	if (copy_from_user(name, path, len)) {
+		kfree(name);
+		return(-EFAULT);
+	}
+
+	error = path_lookup(name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &nd);
+	kfree(name);
+	if (error)
+		return error;
+
+	ASSERT(nd.path.dentry);
+	ASSERT(nd.path.dentry->d_inode);
+
+	inode = igrab(nd.path.dentry->d_inode);
+	path_put(&nd.path);
+
+	dops = dm_fsys_ops(inode->i_sb);
+	if (dops == NULL) {
+		/* No longer in a dmapi-capable filesystem...Toto */
+		iput(inode);
+		return -EINVAL;
+	}
+
+	error = dm_ip_to_handle(inode, &handle);
+	iput(inode);
+
+	if (error)
+		return(error);
+
+	if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL)
+		return(-EBADF);
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	hlen = DM_FSHSIZE;
+	if(copy_to_user(hanp, &handle, (int)hlen))
+		return(-EFAULT);
+	if(put_user(hlen,hlenp))
+		return(-EFAULT);
+	return 0;
+}
+
+
+int
+dm_fd_to_hdl(
+	int		fd,		/* any file descriptor */
+	void		__user *hanp,	/* user's data buffer */
+	size_t		__user *hlenp)	/* set to size of data copied */
+{
+	/* REFERENCED */
+	dm_fsreg_t	*fsrp;
+	dm_handle_t	handle;
+	size_t		hlen;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+	struct file	*filep = fget(fd);
+	struct inode	*ip = filep->f_dentry->d_inode;
+
+	if (!filep)
+		return(-EBADF);
+	if ((error = dm_ip_to_handle(ip, &handle)) != 0)
+		return(error);
+
+	if ((fsrp = dm_find_fsreg_and_lock(&handle.ha_fsid, &lc)) == NULL)
+		return(-EBADF);
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	hlen = DM_HSIZE(handle);
+	if (copy_to_user(hanp, &handle, (int)hlen))
+		return(-EFAULT);
+	fput(filep);
+	if(put_user(hlen, hlenp))
+		return(-EFAULT);
+	return 0;
+}
+
+
+/* Enable events on an object. */
+
+int
+dm_set_eventlist(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_eventset_t	__user *eventsetp,
+	u_int		maxevent)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_eventset_t	eventset;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	if (copy_from_user(&eventset, eventsetp, sizeof(eventset)))
+		return(-EFAULT);
+
+	/* Do some minor sanity checking. */
+
+	if (maxevent == 0 || maxevent > DM_EVENT_MAX)
+		return(-EINVAL);
+
+	/* Access the specified object. */
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->set_eventlist(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0),
+		&eventset, maxevent);
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+/* Return the list of enabled events for an object. */
+
+int
+dm_get_eventlist(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		nelem,
+	dm_eventset_t	__user *eventsetp,
+	u_int		__user *nelemp)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	dm_eventset_t	eventset;
+	u_int		elem;
+	int		error;
+
+	if (nelem == 0)
+		return(-EINVAL);
+
+	/* Access the specified object. */
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* Get the object's event list. */
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->get_eventlist(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0),
+		nelem, &eventset, &elem);
+
+	dm_app_put_tdp(tdp);
+
+	if (error)
+		return(error);
+
+	if (copy_to_user(eventsetp, &eventset, sizeof(eventset)))
+		return(-EFAULT);
+	if (put_user(nelem, nelemp))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* Register for disposition of events.	The handle must either be the
+   global handle or must be the handle of a file system.  The list of events
+   is pointed to by eventsetp.
+*/
+
+int
+dm_set_disp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_eventset_t	__user *eventsetp,
+	u_int		maxevent)
+{
+	dm_session_t	*s;
+	dm_fsreg_t	*fsrp;
+	dm_tokdata_t	*tdp;
+	dm_eventset_t	eventset;
+	int		error;
+	unsigned long	lc1;		/* first lock cookie */
+	unsigned long	lc2;		/* second lock cookie */
+	u_int		i;
+
+	/* Copy in and validate the event mask.	 Only the lower maxevent bits
+	   are meaningful, so clear any bits set above maxevent.
+	*/
+
+	if (maxevent == 0 || maxevent > DM_EVENT_MAX)
+		return(-EINVAL);
+	if (copy_from_user(&eventset, eventsetp, sizeof(eventset)))
+		return(-EFAULT);
+	eventset &= (1 << maxevent) - 1;
+
+	/* If the caller specified the global handle, then the only valid token
+	   is DM_NO_TOKEN, and the only valid event in the event mask is
+	   DM_EVENT_MOUNT.  If it is set, add the session to the list of
+	   sessions that want to receive mount events.	If it is clear, remove
+	   the session from the list.  Since DM_EVENT_MOUNT events never block
+	   waiting for a session to register, there is noone to wake up if we
+	   do add the session to the list.
+	*/
+
+	if (DM_GLOBALHAN(hanp, hlen)) {
+		if (token != DM_NO_TOKEN)
+			return(-EINVAL);
+		if ((error = dm_find_session_and_lock(sid, &s, &lc1)) != 0)
+			return(error);
+		if (eventset == 0) {
+			s->sn_flags &= ~DM_SN_WANTMOUNT;
+			error = 0;
+		} else if (eventset == 1 << DM_EVENT_MOUNT) {
+			s->sn_flags |= DM_SN_WANTMOUNT;
+			error = 0;
+		} else {
+			error = -EINVAL;
+		}
+		mutex_spinunlock(&s->sn_qlock, lc1);
+		return(error);
+	}
+
+	/* Since it's not the global handle, it had better be a filesystem
+	   handle.  Verify that the first 'maxevent' events in the event list
+	   are all valid for a filesystem handle.
+	*/
+
+	if (eventset & ~DM_VALID_DISP_EVENTS)
+		return(-EINVAL);
+
+	/* Verify that the session is valid, that the handle is a filesystem
+	   handle, and that the filesystem is capable of sending events.  (If
+	   a dm_fsreg_t structure exists, then the filesystem can issue events.)
+	*/
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc1);
+	if (fsrp == NULL) {
+		dm_app_put_tdp(tdp);
+		return(-EINVAL);
+	}
+
+	/* Now that we own 'fsrp->fr_lock', get the lock on the session so that
+	   it can't disappear while we add it to the filesystem's event mask.
+	*/
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) {
+		mutex_spinunlock(&fsrp->fr_lock, lc1);
+		dm_app_put_tdp(tdp);
+		return(error);
+	}
+
+	/* Update the event disposition array for this filesystem, adding
+	   and/or removing the session as appropriate.	If this session is
+	   dropping registration for DM_EVENT_DESTROY, or is overriding some
+	   other session's registration for DM_EVENT_DESTROY, then clear any
+	   any attr-on-destroy attribute name also.
+	*/
+
+	for (i = 0; i < DM_EVENT_MAX; i++) {
+		if (DMEV_ISSET(i, eventset)) {
+			if (i == DM_EVENT_DESTROY && fsrp->fr_sessp[i] != s)
+				memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr));
+			fsrp->fr_sessp[i] = s;
+		} else if (fsrp->fr_sessp[i] == s) {
+			if (i == DM_EVENT_DESTROY)
+				memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr));
+			fsrp->fr_sessp[i] = NULL;
+		}
+	}
+	mutex_spinunlock(&s->sn_qlock, lc2);	/* reverse cookie order */
+
+	/* Wake up all processes waiting for a disposition on this filesystem
+	   in case any of them happen to be waiting for an event which we just
+	   added.
+	*/
+
+	if (fsrp->fr_dispcnt)
+		sv_broadcast(&fsrp->fr_dispq);
+
+	mutex_spinunlock(&fsrp->fr_lock, lc1);
+
+	dm_app_put_tdp(tdp);
+	return(0);
+}
+
+
+/*
+ *	Register a specific attribute name with a filesystem.  The value of
+ *	the attribute is to be returned with an asynchronous destroy event.
+ */
+
+int
+dm_set_return_on_destroy(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_attrname_t	__user *attrnamep,
+	dm_boolean_t	enable)
+{
+	dm_attrname_t	attrname;
+	dm_tokdata_t	*tdp;
+	dm_fsreg_t	*fsrp;
+	dm_session_t	*s;
+	int		error;
+	unsigned long	lc1;		/* first lock cookie */
+	unsigned long	lc2;		/* second lock cookie */
+
+	/* If a dm_attrname_t is provided, copy it in and validate it. */
+
+	if (enable && (error = copy_from_user(&attrname, attrnamep, sizeof(attrname))) != 0)
+		return(error);
+
+	/* Validate the filesystem handle and use it to get the filesystem's
+	   disposition structure.
+	*/
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_EXCL, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc1);
+	if (fsrp == NULL) {
+		dm_app_put_tdp(tdp);
+		return(-EINVAL);
+	}
+
+	/* Now that we own 'fsrp->fr_lock', get the lock on the session so that
+	   it can't disappear while we add it to the filesystem's event mask.
+	*/
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) {
+		mutex_spinunlock(&fsrp->fr_lock, lc1);
+		dm_app_put_tdp(tdp);
+		return(error);
+	}
+
+	/* A caller cannot disable return-on-destroy if he is not registered
+	   for DM_EVENT_DESTROY.  Enabling return-on-destroy is an implicit
+	   dm_set_disp() for DM_EVENT_DESTROY; we wake up all processes
+	   waiting for a disposition in case any was waiting for a
+	   DM_EVENT_DESTROY event.
+	*/
+
+	error = 0;
+	if (enable) {
+		fsrp->fr_sessp[DM_EVENT_DESTROY] = s;
+		fsrp->fr_rattr = attrname;
+		if (fsrp->fr_dispcnt)
+			sv_broadcast(&fsrp->fr_dispq);
+	} else if (fsrp->fr_sessp[DM_EVENT_DESTROY] != s) {
+		error = -EINVAL;
+	} else {
+		memset(&fsrp->fr_rattr, 0, sizeof(fsrp->fr_rattr));
+	}
+	mutex_spinunlock(&s->sn_qlock, lc2);	/* reverse cookie order */
+	mutex_spinunlock(&fsrp->fr_lock, lc1);
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_get_mountinfo(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_fsreg_t	*fsrp;
+	dm_tokdata_t	*tdp;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	/* Make sure that the caller's buffer is 8-byte aligned. */
+
+	if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0)
+		return(-EFAULT);
+
+	/* Verify that the handle is a filesystem handle, and that the
+	   filesystem is capable of sending events.  If not, return an error.
+	*/
+
+	error = dm_app_get_tdp(sid, hanp, hlen, token, DM_TDT_VFS,
+		DM_RIGHT_SHARED, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* Find the filesystem entry.  This should always succeed as the
+	   dm_app_get_tdp call created a filesystem reference.	Once we find
+	   the entry, drop the lock.  The mountinfo message is never modified,
+	   the filesystem entry can't disappear, and we don't want to hold a
+	   spinlock while doing copyout calls.
+	*/
+
+	fsrp = dm_find_fsreg_and_lock(&tdp->td_handle.ha_fsid, &lc);
+	if (fsrp == NULL) {
+		dm_app_put_tdp(tdp);
+		return(-EINVAL);
+	}
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+
+	/* Copy the message into the user's buffer and update his 'rlenp'. */
+
+	if (put_user(fsrp->fr_msgsize, rlenp)) {
+		error = -EFAULT;
+	} else if (fsrp->fr_msgsize > buflen) { /* user buffer not big enough */
+		error = -E2BIG;
+	} else if (copy_to_user(bufp, fsrp->fr_msg, fsrp->fr_msgsize)) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_getall_disp(
+	dm_sessid_t	sid,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user  *rlenp)
+{
+	dm_session_t	*s;		/* pointer to session given by sid */
+	unsigned long	lc1;		/* first lock cookie */
+	unsigned long	lc2;		/* second lock cookie */
+	int		totalsize;
+	int		msgsize;
+	int		fsyscnt;
+	dm_dispinfo_t	*prevmsg;
+	dm_fsreg_t	*fsrp;
+	int		error;
+	char		*kbuf;
+
+	int tmp3;
+	int tmp4;
+
+	/* Because the dm_getall_disp structure contains a __u64 field,
+	   make sure that the buffer provided by the caller is aligned so
+	   that he can read such fields successfully.
+	*/
+
+	if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0)
+		return(-EFAULT);
+
+	/* Compute the size of a dm_dispinfo structure, rounding up to an
+	   8-byte boundary so that any subsequent structures will also be
+	   aligned.
+	*/
+
+#if 0
+	/* XXX	ug, what is going on here? */
+	msgsize = (sizeof(dm_dispinfo_t) + DM_FSHSIZE + sizeof(uint64_t) - 1) &
+		~(sizeof(uint64_t) - 1);
+#else
+	tmp3 = sizeof(dm_dispinfo_t) + DM_FSHSIZE;
+	tmp3 += sizeof(__u64);
+	tmp3 -= 1;
+	tmp4 = ~((int)sizeof(__u64) - 1);
+	msgsize = tmp3 & tmp4;
+#endif
+
+	/* Loop until we can get the right amount of temp space, being careful
+	   not to hold a mutex during the allocation.  Usually only one trip.
+	*/
+
+	for (;;) {
+		if ((fsyscnt = dm_fsys_cnt) == 0) {
+			/*if (dm_cpoutsizet(rlenp, 0))*/
+			if (put_user(0,rlenp))
+				return(-EFAULT);
+			return(0);
+		}
+		kbuf = kmalloc(fsyscnt * msgsize, GFP_KERNEL);
+		if (kbuf == NULL) {
+			printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+			return -ENOMEM;
+		}
+
+		lc1 = mutex_spinlock(&dm_reg_lock);
+		if (fsyscnt == dm_fsys_cnt)
+			break;
+
+		mutex_spinunlock(&dm_reg_lock, lc1);
+		kfree(kbuf);
+	}
+
+	/* Find the indicated session and lock it. */
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc2)) != 0) {
+		mutex_spinunlock(&dm_reg_lock, lc1);
+		kfree(kbuf);
+		return(error);
+	}
+
+	/* Create a dm_dispinfo structure for each filesystem in which
+	   this session has at least one event selected for disposition.
+	*/
+
+	totalsize = 0;		/* total bytes to transfer to the user */
+	prevmsg = NULL;
+
+	for (fsrp = dm_registers; fsrp; fsrp = fsrp->fr_next) {
+		dm_dispinfo_t	*disp;
+		int		event;
+		int		found;
+
+		disp = (dm_dispinfo_t *)(kbuf + totalsize);
+
+		DMEV_ZERO(disp->di_eventset);
+
+		for (event = 0, found = 0; event < DM_EVENT_MAX; event++) {
+			if (fsrp->fr_sessp[event] != s)
+				continue;
+			DMEV_SET(event, disp->di_eventset);
+			found++;
+		}
+		if (!found)
+			continue;
+
+		disp->_link = 0;
+		disp->di_fshandle.vd_offset = sizeof(dm_dispinfo_t);
+		disp->di_fshandle.vd_length = DM_FSHSIZE;
+
+		memcpy((char *)disp + disp->di_fshandle.vd_offset,
+			&fsrp->fr_fsid, disp->di_fshandle.vd_length);
+
+		if (prevmsg)
+			prevmsg->_link = msgsize;
+
+		prevmsg = disp;
+		totalsize += msgsize;
+	}
+	mutex_spinunlock(&s->sn_qlock, lc2);	/* reverse cookie order */
+	mutex_spinunlock(&dm_reg_lock, lc1);
+
+	if (put_user(totalsize, rlenp)) {
+		error = -EFAULT;
+	} else if (totalsize > buflen) {	/* no more room */
+		error = -E2BIG;
+	} else if (totalsize && copy_to_user(bufp, kbuf, totalsize)) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+
+	kfree(kbuf);
+	return(error);
+}
+
+int
+dm_open_by_handle_rvp(
+	unsigned int	fd,
+	void		__user *hanp,
+	size_t		hlen,
+	int		flags,
+	int		*rvp)
+{
+	const struct cred *cred = current_cred();
+	dm_handle_t	handle;
+	int		error;
+	short		td_type;
+	struct dentry	*dentry;
+	struct inode	*inodep;
+	int		new_fd;
+	struct file	*mfilp;
+	struct file	*filp;
+
+	if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0) {
+		return(error);
+	}
+
+	if ((inodep = dm_handle_to_ip(&handle, &td_type)) == NULL) {
+		return(-EBADF);
+	}
+	if ((td_type == DM_TDT_VFS) || (td_type == DM_TDT_OTH)) {
+		iput(inodep);
+		return(-EBADF);
+	}
+
+	if ((new_fd = get_unused_fd()) < 0) {
+		iput(inodep);
+		return(-EMFILE);
+	}
+
+	dentry = d_obtain_alias(inodep);
+	if (dentry == NULL) {
+		iput(inodep);
+		put_unused_fd(new_fd);
+		return(-ENOMEM);
+	}
+
+	mfilp = fget(fd);
+	if (!mfilp) {
+		dput(dentry);
+		put_unused_fd(new_fd);
+		return(-EBADF);
+	}
+
+	mntget(mfilp->f_vfsmnt);
+
+	/* Create file pointer */
+	filp = dentry_open(dentry, mfilp->f_vfsmnt, flags, cred);
+	if (IS_ERR(filp)) {
+		put_unused_fd(new_fd);
+		fput(mfilp);
+		return PTR_ERR(filp);
+	}
+
+	if (td_type == DM_TDT_REG)
+		filp->f_mode |= FMODE_NOCMTIME;
+
+	fd_install(new_fd, filp);
+	fput(mfilp);
+	*rvp = new_fd;
+	return 0;
+}
+
+
+#ifdef HAVE_DM_QUEUE_FLUSH
+/* Find the threads that have a reference to our filesystem and force
+   them to return with the specified errno.
+   We look for them in each dm_fsreg_t's fr_evt_dispq.
+*/
+
+int
+dm_release_disp_threads(
+	dm_fsid_t	*fsidp,
+	struct inode	*inode, /* may be null */
+	int		errno)
+{
+	unsigned long	lc;
+	dm_fsreg_t	*fsrp;
+	dm_tokevent_t	*tevp;
+	dm_tokdata_t	*tdp;
+	dm_eventq_t	*queue;
+	int		found_events = 0;
+
+	if ((fsrp = dm_find_fsreg_and_lock(fsidp, &lc)) == NULL){
+		return 0;
+	}
+
+	queue = &fsrp->fr_evt_dispq;
+	for (tevp = queue->eq_head; tevp; tevp = tevp->te_next) {
+		nested_spinlock(&tevp->te_lock);
+		if (inode) {
+			for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) {
+				if( tdp->td_ip == inode ) {
+					tevp->te_flags |= DM_TEF_FLUSH;
+					tevp->te_reply = errno;
+					found_events = 1;
+					break;
+				}
+			}
+		}
+		else {
+			tevp->te_flags |= DM_TEF_FLUSH;
+			tevp->te_reply = errno;
+			found_events = 1;
+		}
+		nested_spinunlock(&tevp->te_lock);
+	}
+
+	if (found_events && fsrp->fr_dispcnt)
+		sv_broadcast(&fsrp->fr_dispq);
+	mutex_spinunlock(&fsrp->fr_lock, lc);
+	return 0;
+}
+#endif /* HAVE_DM_QUEUE_FLUSH */
--- /dev/null
+++ b/fs/dmapi/dmapi_right.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#include <asm/uaccess.h>
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+
+#define DM_FG_STHREAD		0x001	/* keep other threads from using tdp */
+#define DM_FG_MUSTEXIST		0x002	/* handle must exist in the event */
+#define DM_FG_DONTADD		0x004	/* don't add handle if not in event */
+
+/* Get a handle of the form (void *, size_t) from user space and convert it to
+   a handle_t.	Do as much validation of the result as possible; any error
+   other than a bad address should return EBADF per the DMAPI spec.
+*/
+
+int
+dm_copyin_handle(
+	void		__user *hanp,	/* input,  handle data */
+	size_t		hlen,		/* input,  size of handle data */
+	dm_handle_t	*handlep)	/* output, copy of data */
+{
+	u_short		len;
+	dm_fid_t	*fidp;
+
+	fidp = (dm_fid_t*)&handlep->ha_fid;
+
+	if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
+		return -EBADF;
+
+	if (copy_from_user(handlep, hanp, hlen))
+		return -EFAULT;
+
+	if (hlen < sizeof(*handlep))
+		memset((char *)handlep + hlen, 0, sizeof(*handlep) - hlen);
+
+	if (hlen == sizeof(handlep->ha_fsid))
+		return 0;	/* FS handle, nothing more to check */
+
+	len = hlen - sizeof(handlep->ha_fsid) - sizeof(fidp->dm_fid_len);
+
+	if ((fidp->dm_fid_len != len) || fidp->dm_fid_pad)
+		return -EBADF;
+	return 0;
+}
+
+/* Allocate and initialize a tevp structure.  Called from both application and
+   event threads.
+*/
+
+static dm_tokevent_t *
+dm_init_tevp(
+	int		ev_size,	/* size of event structure */
+	int		var_size)	/* size of variable-length data */
+{
+	dm_tokevent_t	*tevp;
+	int		msgsize;
+
+	/* Calculate the size of the event in bytes and allocate memory for it.
+	   Zero all but the variable portion of the message, which will be
+	   eventually overlaid by the caller with data.
+	*/
+
+	msgsize = offsetof(dm_tokevent_t, te_event) + ev_size + var_size;
+	tevp = kmalloc(msgsize, GFP_KERNEL);
+	if (tevp == NULL) {
+		printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+		return NULL;
+	}
+	memset(tevp, 0, msgsize - var_size);
+
+	/* Now initialize all the non-zero fields. */
+
+	spinlock_init(&tevp->te_lock, "te_lock");
+	sv_init(&tevp->te_evt_queue, SV_DEFAULT, "te_evt_queue");
+	sv_init(&tevp->te_app_queue, SV_DEFAULT, "te_app_queue");
+	tevp->te_allocsize = msgsize;
+	tevp->te_msg.ev_type = DM_EVENT_INVALID;
+	tevp->te_flags = 0;
+
+	return(tevp);
+}
+
+
+/* Given the event type and the number of bytes of variable length data that
+   will follow the event, dm_evt_create_tevp() creates a dm_tokevent_t
+   structure to hold the event and initializes all the common event fields.
+
+   No locking is required for this routine because the caller is an event
+   thread, and is therefore the only thread that can see the event.
+*/
+
+dm_tokevent_t *
+dm_evt_create_tevp(
+	dm_eventtype_t	event,
+	int		variable_size,
+	void		**msgpp)
+{
+	dm_tokevent_t	*tevp;
+	int		evsize;
+
+	switch (event) {
+	case DM_EVENT_READ:
+	case DM_EVENT_WRITE:
+	case DM_EVENT_TRUNCATE:
+		evsize = sizeof(dm_data_event_t);
+		break;
+
+	case DM_EVENT_DESTROY:
+		evsize = sizeof(dm_destroy_event_t);
+		break;
+
+	case DM_EVENT_MOUNT:
+		evsize = sizeof(dm_mount_event_t);
+		break;
+
+	case DM_EVENT_PREUNMOUNT:
+	case DM_EVENT_UNMOUNT:
+	case DM_EVENT_NOSPACE:
+	case DM_EVENT_CREATE:
+	case DM_EVENT_REMOVE:
+	case DM_EVENT_RENAME:
+	case DM_EVENT_SYMLINK:
+	case DM_EVENT_LINK:
+	case DM_EVENT_POSTCREATE:
+	case DM_EVENT_POSTREMOVE:
+	case DM_EVENT_POSTRENAME:
+	case DM_EVENT_POSTSYMLINK:
+	case DM_EVENT_POSTLINK:
+	case DM_EVENT_ATTRIBUTE:
+	case DM_EVENT_DEBUT:		/* currently not supported */
+	case DM_EVENT_CLOSE:		/* currently not supported */
+		evsize = sizeof(dm_namesp_event_t);
+		break;
+
+	case DM_EVENT_CANCEL:		/* currently not supported */
+		evsize = sizeof(dm_cancel_event_t);
+		break;
+
+	case DM_EVENT_USER:
+		evsize = 0;
+		break;
+
+	default:
+		panic("dm_create_tevp: called with unknown event type %d\n",
+			event);
+	}
+
+	/* Allocate and initialize an event structure of the correct size. */
+
+	tevp = dm_init_tevp(evsize, variable_size);
+	if (tevp == NULL)
+		return NULL;
+	tevp->te_evt_ref = 1;
+
+	/* Fields ev_token, ev_sequence, and _link are all filled in when the
+	   event is queued onto a session.  Initialize all other fields here.
+	*/
+
+	tevp->te_msg.ev_type = event;
+	tevp->te_msg.ev_data.vd_offset = offsetof(dm_tokevent_t, te_event) -
+		offsetof(dm_tokevent_t, te_msg);
+	tevp->te_msg.ev_data.vd_length = evsize + variable_size;
+
+	/* Give the caller a pointer to the event-specific structure. */
+
+	*msgpp = ((char *)&tevp->te_msg + tevp->te_msg.ev_data.vd_offset);
+	return(tevp);
+}
+
+
+/* Given a pointer to an event (tevp) and a pointer to a handle_t, look for a
+   tdp structure within the event which contains the handle_t.	Either verify
+   that the event contains the tdp, or optionally add the tdp to the
+   event.  Called only from application threads.
+
+   On entry, tevp->te_lock is held; it is dropped prior to return.
+*/
+
+static int
+dm_app_lookup_tdp(
+	dm_handle_t	*handlep,	/* the handle we are looking for */
+	dm_tokevent_t	*tevp,		/* the event to search for the handle */
+	unsigned long	*lcp,		/* address of active lock cookie */
+	short		types,		/* acceptable object types */
+	dm_right_t	right,		/* minimum right the object must have */
+	u_int		flags,
+	dm_tokdata_t	**tdpp)		/* if ! NULL, pointer to matching tdp */
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	struct inode	*ip;
+	int		error;
+
+	/* Bump the tevp application reference counter so that the event
+	   can't disappear in case we have to drop the lock for a while.
+	*/
+
+	tevp->te_app_ref++;
+	*tdpp = NULL;		/* assume failure */
+
+	for (;;) {
+		/* Look for a matching tdp in the tevp. */
+
+		for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) {
+			if (DM_HANDLE_CMP(&tdp->td_handle, handlep) == 0)
+				break;
+		}
+
+		/* If the tdp exists, but either we need single-thread access
+		   to the handle and can't get it, or some other thread already
+		   has single-thread access, then sleep until we can try again.
+		*/
+
+		if (tdp != NULL && tdp->td_app_ref &&
+		    ((flags & DM_FG_STHREAD) ||
+		     (tdp->td_flags & DM_TDF_STHREAD))) {
+			tevp->te_app_slp++;
+			sv_wait(&tevp->te_app_queue, 1,
+				&tevp->te_lock, *lcp);
+			*lcp = mutex_spinlock(&tevp->te_lock);
+			tevp->te_app_slp--;
+			continue;
+		}
+
+		if (tdp != NULL &&
+		    (tdp->td_vcount > 0 || tdp->td_flags & DM_TDF_EVTREF)) {
+			/* We have an existing tdp with a non-zero inode
+			   reference count.  If it's the wrong type, return
+			   an appropriate errno.
+			*/
+
+			if (!(tdp->td_type & types)) {
+				mutex_spinunlock(&tevp->te_lock, *lcp);
+				dm_put_tevp(tevp, NULL); /* no destroy events */
+				return(-EOPNOTSUPP);
+			}
+
+			/* If the current access right isn't high enough,
+			   complain.
+			*/
+
+			if (tdp->td_right < right) {
+				mutex_spinunlock(&tevp->te_lock, *lcp);
+				dm_put_tevp(tevp, NULL); /* no destroy events */
+				return(-EACCES);
+			}
+
+			/* The handle is acceptable.  Increment the tdp
+			   application and inode references and mark the tdp
+			   as single-threaded if necessary.
+			*/
+
+			tdp->td_app_ref++;
+			if (flags & DM_FG_STHREAD)
+				tdp->td_flags |= DM_TDF_STHREAD;
+			tdp->td_vcount++;
+
+			fsys_vector = dm_fsys_vector(tdp->td_ip);
+			(void)fsys_vector->obj_ref_hold(tdp->td_ip);
+
+			mutex_spinunlock(&tevp->te_lock, *lcp);
+			*tdpp = tdp;
+			return(0);
+		}
+
+		/* If the tdp is not in the tevp or does not have an inode
+		   reference, check to make sure it is okay to add/update it.
+		*/
+
+		if (flags & DM_FG_MUSTEXIST) {
+			mutex_spinunlock(&tevp->te_lock, *lcp);
+			dm_put_tevp(tevp, NULL);	/* no destroy events */
+			return(-EACCES);		/* i.e. an insufficient right */
+		}
+		if (flags & DM_FG_DONTADD) {
+			tevp->te_app_ref--;
+			mutex_spinunlock(&tevp->te_lock, *lcp);
+			return(0);
+		}
+
+		/* If a tdp structure doesn't yet exist, create one and link
+		   it into the tevp.  Drop the lock while we are doing this as
+		   zallocs can go to sleep.  Once we have the memory, make
+		   sure that another thread didn't simultaneously add the same
+		   handle to the same event.  If so, toss ours and start over.
+		*/
+
+		if (tdp == NULL) {
+			dm_tokdata_t	*tmp;
+
+			mutex_spinunlock(&tevp->te_lock, *lcp);
+
+			tdp = kmem_cache_alloc(dm_tokdata_cachep, GFP_KERNEL);
+			if (tdp == NULL){
+				printk("%s/%d: kmem_cache_alloc(dm_tokdata_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+				return(-ENOMEM);
+			}
+			memset(tdp, 0, sizeof(*tdp));
+
+			*lcp = mutex_spinlock(&tevp->te_lock);
+
+			for (tmp = tevp->te_tdp; tmp; tmp = tmp->td_next) {
+				if (DM_HANDLE_CMP(&tmp->td_handle, handlep) == 0)
+					break;
+			}
+			if (tmp) {
+				kmem_cache_free(dm_tokdata_cachep, tdp);
+				continue;
+			}
+
+			tdp->td_next = tevp->te_tdp;
+			tevp->te_tdp = tdp;
+			tdp->td_tevp = tevp;
+			tdp->td_handle = *handlep;
+		}
+
+		/* Temporarily single-thread access to the tdp so that other
+		   threads don't touch it while we are filling the rest of the
+		   fields in.
+		*/
+
+		tdp->td_app_ref = 1;
+		tdp->td_flags |= DM_TDF_STHREAD;
+
+		/* Drop the spinlock while we access, validate, and obtain the
+		   proper rights to the object.	 This can take a very long time
+		   if the inode is not in memory, if the filesystem is
+		   unmounting,	or if the request_right() call should block
+		   because some other tdp or kernel thread is holding a right.
+		*/
+
+		mutex_spinunlock(&tevp->te_lock, *lcp);
+
+		if ((ip = dm_handle_to_ip(handlep, &tdp->td_type)) == NULL) {
+			error = -EBADF;
+		} else {
+			tdp->td_vcount = 1;
+			tdp->td_ip = ip;
+
+			/* The handle is usable.  Check that the type of the
+			   object matches one of the types that the caller
+			   will accept.
+			*/
+
+			if (!(types & tdp->td_type)) {
+				error = -EOPNOTSUPP;
+			} else if (right > DM_RIGHT_NULL) {
+				/* Attempt to get the rights required by the
+				   caller.  If rights can't be obtained, return
+				   an error.
+				*/
+
+				fsys_vector = dm_fsys_vector(tdp->td_ip);
+				error = fsys_vector->request_right(tdp->td_ip,
+					DM_RIGHT_NULL,
+					(tdp->td_type == DM_TDT_VFS ?
+					DM_FSYS_OBJ : 0),
+					DM_RR_WAIT, right);
+				if (!error) {
+					tdp->td_right = right;
+				}
+			} else {
+				error = 0;
+			}
+		}
+		if (error != 0) {
+			dm_put_tevp(tevp, tdp); /* destroy event risk, although tiny */
+			return(error);
+		}
+
+		*lcp = mutex_spinlock(&tevp->te_lock);
+
+		/* Wake up any threads which may have seen our tdp while we
+		   were filling it in.
+		*/
+
+		if (!(flags & DM_FG_STHREAD)) {
+			tdp->td_flags &= ~DM_TDF_STHREAD;
+			if (tevp->te_app_slp)
+				sv_broadcast(&tevp->te_app_queue);
+		}
+
+		mutex_spinunlock(&tevp->te_lock, *lcp);
+		*tdpp = tdp;
+		return(0);
+	}
+}
+
+
+/* dm_app_get_tdp_by_token() is called whenever the application request
+   contains a session ID and contains a token other than DM_NO_TOKEN.
+   Most of the callers provide a right that is either DM_RIGHT_SHARED or
+   DM_RIGHT_EXCL, but a few of the callers such as dm_obj_ref_hold() may
+   specify a right of DM_RIGHT_NULL.
+*/
+
+static int
+dm_app_get_tdp_by_token(
+	dm_sessid_t	sid,		/* an existing session ID */
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,		/* an existing token */
+	short		types,		/* acceptable object types */
+	dm_right_t	right,		/* minimum right the object must have */
+	u_int		flags,
+	dm_tokdata_t	**tdpp)
+{
+	dm_tokevent_t	*tevp;
+	dm_handle_t	handle;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	if (right < DM_RIGHT_NULL || right > DM_RIGHT_EXCL)
+		return(-EINVAL);
+
+	if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0)
+		return(error);
+
+	/* Find and lock the event which corresponds to the specified
+	   session/token pair.
+	*/
+
+	if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0)
+		return(error);
+
+	return(dm_app_lookup_tdp(&handle, tevp, &lc, types,
+		right, flags, tdpp));
+}
+
+
+/* Function dm_app_get_tdp() must ONLY be called from routines associated with
+   application calls, e.g. dm_read_invis, dm_set_disp, etc.  It must not be
+   called by a thread responsible for generating an event such as
+   dm_send_data_event()!
+
+   dm_app_get_tdp() is the interface used by all application calls other than
+   dm_get_events, dm_respond_event, dm_get_config, dm_get_config_events, and by
+   the dm_obj_ref_* and dm_*_right families of requests.
+
+   dm_app_get_tdp() converts a sid/hanp/hlen/token quad into a tdp pointer,
+   increments the number of active application threads in the event, and
+   increments the number of active application threads using the tdp.  The
+   'right' parameter must be either DM_RIGHT_SHARED or DM_RIGHT_EXCL.  The
+   token may either be DM_NO_TOKEN, or can be a token received in a synchronous
+   event.
+*/
+
+int
+dm_app_get_tdp(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	short		types,
+	dm_right_t	right,		/* minimum right */
+	dm_tokdata_t	**tdpp)
+{
+	dm_session_t	*s;
+	dm_handle_t	handle;
+	dm_tokevent_t	*tevp;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	ASSERT(right >= DM_RIGHT_SHARED);
+
+	/* If a token other than DM_NO_TOKEN is specified, find the event on
+	   this session which owns the token and increment its reference count.
+	*/
+
+	if (token != DM_NO_TOKEN) {	/* look up existing tokevent struct */
+		return(dm_app_get_tdp_by_token(sid, hanp, hlen, token, types,
+			right, DM_FG_MUSTEXIST, tdpp));
+	}
+
+	/* The token is DM_NO_TOKEN.  In this case we only want to verify that
+	   the session ID is valid, and do not need to continue holding the
+	   session lock after we know that to be true.
+	*/
+
+	if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0)
+		return(error);
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0)
+		return(error);
+	mutex_spinunlock(&s->sn_qlock, lc);
+
+	/* When DM_NO_TOKEN is used, we simply block until we can obtain the
+	   right that we want (since the tevp contains no tdp structures).
+	   The blocking when we eventually support it will occur within
+	   fsys_vector->request_right().
+	*/
+
+	tevp = dm_init_tevp(0, 0);
+	lc = mutex_spinlock(&tevp->te_lock);
+
+	return(dm_app_lookup_tdp(&handle, tevp, &lc, types, right, 0, tdpp));
+}
+
+
+/* dm_get_config_tdp() is only called by dm_get_config() and
+   dm_get_config_events(), which neither have a session ID nor a token.
+   Both of these calls are supposed to work even if the filesystem is in the
+   process of being mounted, as long as the caller only uses handles within
+   the mount event.
+*/
+
+int
+dm_get_config_tdp(
+	void		__user *hanp,
+	size_t		hlen,
+	dm_tokdata_t	**tdpp)
+{
+	dm_handle_t	handle;
+	dm_tokevent_t	*tevp;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	if ((error = dm_copyin_handle(hanp, hlen, &handle)) != 0)
+		return(error);
+
+	tevp = dm_init_tevp(0, 0);
+	lc = mutex_spinlock(&tevp->te_lock);
+
+	/* Try to use the handle provided by the caller and assume DM_NO_TOKEN.
+	   This will fail if the filesystem is in the process of being mounted.
+	*/
+
+	error = dm_app_lookup_tdp(&handle, tevp, &lc, DM_TDT_ANY,
+		DM_RIGHT_NULL, 0, tdpp);
+
+	if (!error) {
+		return(0);
+	}
+
+	/* Perhaps the filesystem is still mounting, in which case we need to
+	   see if this is one of the handles in the DM_EVENT_MOUNT tevp.
+	*/
+
+	if ((tevp = dm_find_mount_tevp_and_lock(&handle.ha_fsid, &lc)) == NULL)
+		return(-EBADF);
+
+	return(dm_app_lookup_tdp(&handle, tevp, &lc, DM_TDT_ANY,
+		DM_RIGHT_NULL, DM_FG_MUSTEXIST, tdpp));
+}
+
+
+/* dm_put_tdp() is called to release any right held on the inode, and to
+   VN_RELE() all references held on the inode.	It is the caller's
+   responsibility to ensure that no other application threads are using the
+   tdp, and if necessary to unlink the tdp from the tevp before calling
+   this routine and to free the tdp afterwards.
+*/
+
+static void
+dm_put_tdp(
+	dm_tokdata_t	*tdp)
+{
+	ASSERT(tdp->td_app_ref <= 1);
+
+	/* If the application thread is holding a right, or if the event
+	   thread had a right but it has disappeared because of a dm_pending
+	   or Cntl-C, then we need to release it here.
+	*/
+
+	if (tdp->td_right != DM_RIGHT_NULL) {
+		dm_fsys_vector_t *fsys_vector;
+
+		fsys_vector = dm_fsys_vector(tdp->td_ip);
+		(void)fsys_vector->release_right(tdp->td_ip, tdp->td_right,
+			(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0));
+		tdp->td_right = DM_RIGHT_NULL;
+	}
+
+	/* Given that we wouldn't be here if there was still an event thread,
+	   this VN_RELE loop has the potential of generating a DM_EVENT_DESTROY
+	   event if some other thread has unlinked the file.
+	*/
+
+	while (tdp->td_vcount > 0) {
+		iput(tdp->td_ip);
+		tdp->td_vcount--;
+	}
+
+	tdp->td_flags &= ~(DM_TDF_HOLD|DM_TDF_RIGHT);
+	tdp->td_ip = NULL;
+}
+
+
+/* Function dm_put_tevp() must ONLY be called from routines associated with
+   application threads, e.g. dm_read_invis, dm_get_events, etc.	 It must not be
+   called by a thread responsible for generating an event, such as
+   dm_send_data_event.
+
+   PLEASE NOTE: It is possible for this routine to generate DM_EVENT_DESTROY
+   events, because its calls to dm_put_tdp drop inode references, and another
+   thread may have already unlinked a file whose inode we are de-referencing.
+   This sets the stage for various types of deadlock if the thread calling
+   dm_put_tevp is the same thread that calls dm_respond_event!	In particular,
+   the dm_sent_destroy_event routine needs to obtain the dm_reg_lock,
+   dm_session_lock, and sn_qlock in order to queue the destroy event.  No
+   caller of dm_put_tevp can hold any of these locks!
+
+   Other possible deadlocks are that dm_send_destroy_event could block waiting
+   for a thread to register for the event using	 dm_set_disp() and/or
+   dm_set_return_on_destroy, or it could block because the session's sn_newq
+   is at the dm_max_queued_msgs event limit.  The only safe solution
+   (unimplemented) is to have a separate kernel thread for each filesystem
+   whose only job is to do the inode-dereferencing.  That way dm_respond_event
+   will not block, so the application can keep calling dm_get_events to read
+   events even if the filesystem thread should block.  (If the filesystem
+   thread blocks, so will all subsequent destroy events for the same
+   filesystem.)
+*/
+
+void
+dm_put_tevp(
+	dm_tokevent_t	*tevp,
+	dm_tokdata_t	*tdp)
+{
+	int		free_tdp = 0;
+	unsigned long	lc;		/* lock cookie */
+
+	lc = mutex_spinlock(&tevp->te_lock);
+
+	if (tdp != NULL) {
+		if (tdp->td_vcount > 1 || (tdp->td_flags & DM_TDF_EVTREF)) {
+			ASSERT(tdp->td_app_ref > 0);
+
+			iput(tdp->td_ip);
+			tdp->td_vcount--;
+		} else {
+			ASSERT(tdp->td_app_ref == 1);
+
+			/* The inode reference count is either already at
+			   zero (e.g. a failed dm_handle_to_ip() call in
+			   dm_app_lookup_tdp()) or is going to zero.  We can't
+			   hold the lock while we decrement the count because
+			   we could potentially end up being busy for a long
+			   time in VOP_INACTIVATE.  Use single-threading to
+			   lock others out while we clean house.
+			*/
+
+			tdp->td_flags |= DM_TDF_STHREAD;
+
+			/* WARNING - A destroy event is possible here if we are
+			   giving up the last reference on an inode which has
+			   been previously unlinked by some other thread!
+			*/
+
+			mutex_spinunlock(&tevp->te_lock, lc);
+			dm_put_tdp(tdp);
+			lc = mutex_spinlock(&tevp->te_lock);
+
+			/* If this tdp is not one of the original tdps in the
+			   event, then remove it from the tevp.
+			*/
+
+			if (!(tdp->td_flags & DM_TDF_ORIG)) {
+				dm_tokdata_t	**tdpp = &tevp->te_tdp;
+
+				while (*tdpp && *tdpp != tdp) {
+					tdpp = &(*tdpp)->td_next;
+				}
+				if (*tdpp == NULL) {
+					panic("dm_remove_tdp_from_tevp: tdp "
+						"%p not in tevp %p\n", tdp,
+						tevp);
+				}
+				*tdpp = tdp->td_next;
+				free_tdp++;
+			}
+		}
+
+		/* If this is the last app thread actively using the tdp, clear
+		   any single-threading and wake up any other app threads who
+		   might be waiting to use this tdp, single-threaded or
+		   otherwise.
+		*/
+
+		if (--tdp->td_app_ref == 0) {
+			if (tdp->td_flags & DM_TDF_STHREAD) {
+				tdp->td_flags &= ~DM_TDF_STHREAD;
+				if (tevp->te_app_slp)
+					sv_broadcast(&tevp->te_app_queue);
+			}
+		}
+
+		if (free_tdp) {
+			kmem_cache_free(dm_tokdata_cachep, tdp);
+		}
+	}
+
+	/* If other application threads are using this token/event, they will
+	   do the cleanup.
+	*/
+
+	if (--tevp->te_app_ref > 0) {
+		mutex_spinunlock(&tevp->te_lock, lc);
+		return;
+	}
+
+	/* If event generation threads are waiting for this thread to go away,
+	   wake them up and let them do the cleanup.
+	*/
+
+	if (tevp->te_evt_ref > 0) {
+		sv_broadcast(&tevp->te_evt_queue);
+		mutex_spinunlock(&tevp->te_lock, lc);
+		return;
+	}
+
+	/* This thread is the last active thread using the token/event.	 No
+	   lock can be held while we disassemble the tevp because we could
+	   potentially end up being busy for a long time in VOP_INACTIVATE.
+	*/
+
+	mutex_spinunlock(&tevp->te_lock, lc);
+
+	/* WARNING - One or more destroy events are possible here if we are
+	   giving up references on inodes which have been previously unlinked
+	   by other kernel threads!
+	*/
+
+	while ((tdp = tevp->te_tdp) != NULL) {
+		tevp->te_tdp = tdp->td_next;
+		dm_put_tdp(tdp);
+		kmem_cache_free(dm_tokdata_cachep, tdp);
+	}
+	spinlock_destroy(&tevp->te_lock);
+	sv_destroy(&tevp->te_evt_queue);
+	sv_destroy(&tevp->te_app_queue);
+	kfree(tevp);
+}
+
+
+/* No caller of dm_app_put_tevp can hold either of the locks dm_reg_lock,
+   dm_session_lock, or any sn_qlock!  (See dm_put_tevp for details.)
+*/
+
+void
+dm_app_put_tdp(
+	dm_tokdata_t	*tdp)
+{
+	dm_put_tevp(tdp->td_tevp, tdp);
+}
+
+
+/* dm_change_right is only called if the event thread is the one doing the
+   cleanup on a completed event.  It looks at the current rights of a tdp
+   and compares that with the rights it had on the tdp when the event was
+   created.  If different, it reaquires the original rights, then transfers
+   the rights back to being thread-based.
+*/
+
+static void
+dm_change_right(
+	dm_tokdata_t	*tdp)
+{
+#ifdef HAVE_DMAPI_RIGHTS
+	dm_fsys_vector_t *fsys_vector;
+	int		error;
+	u_int		type;
+#endif
+
+	/* If the event doesn't have an inode reference, if the original right
+	   was DM_RIGHT_NULL, or if the rights were never switched from being
+	   thread-based to tdp-based, then there is nothing to do.
+	*/
+
+	if (!(tdp->td_flags & DM_TDF_EVTREF))
+		return;
+
+	if (tdp->td_orig_right == DM_RIGHT_NULL)
+		return;
+
+	/* DEBUG - Need a check here for event-based rights. */
+
+#ifdef HAVE_DMAPI_RIGHTS
+	/* The "rights" vectors are stubs now anyway.  When they are
+	 * implemented then bhv locking will have to be sorted out.
+	 */
+
+	/* If the current right is not the same as it was when the event was
+	   created, first get back the original right.
+	*/
+
+	if (tdp->td_right != tdp->td_orig_right) {
+		fsys_vector = dm_fsys_vector(tdp->td_ip);
+		type = (tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0);
+
+		switch (tdp->td_orig_right) {
+		case DM_RIGHT_SHARED:
+			if (tdp->td_right == DM_RIGHT_EXCL) {
+				error = fsys_vector->downgrade_right(
+					tdp->td_ip, tdp->td_right, type);
+				if (!error)
+					break;
+				(void)fsys_vector->release_right(tdp->td_ip,
+					tdp->td_right, type);
+			}
+			(void)fsys_vector->request_right(tdp->td_ip,
+				tdp->td_right, type, DM_RR_WAIT,
+				tdp->td_orig_right);
+			break;
+
+		case DM_RIGHT_EXCL:
+			if (tdp->td_right == DM_RIGHT_SHARED) {
+				error = fsys_vector->upgrade_right(tdp->td_ip,
+					tdp->td_right, type);
+				if (!error)
+					break;
+				(void)fsys_vector->release_right(tdp->td_ip,
+					tdp->td_right, type);
+			}
+			(void)fsys_vector->request_right(tdp->td_ip,
+				tdp->td_right, type, DM_RR_WAIT,
+				tdp->td_orig_right);
+			break;
+		case DM_RIGHT_NULL:
+			break;
+		}
+	}
+#endif
+
+	/* We now have back the same level of rights as we had when the event
+	   was generated.  Now transfer the rights from being tdp-based back
+	   to thread-based.
+	*/
+
+	/* DEBUG - Add a call here to transfer rights back to thread-based. */
+
+	/* Finally, update the tdp so that we don't mess with the rights when
+	   we eventually call dm_put_tdp.
+	*/
+
+	tdp->td_right = DM_RIGHT_NULL;
+}
+
+
+/* This routine is only called by event threads.  The calls to dm_put_tdp
+   are not a deadlock risk here because this is an event thread, and it is
+   okay for such a thread to block on an induced destroy event.	 Okay, maybe
+   there is a slight risk; say that the event contains three inodes all of
+   which have DM_RIGHT_EXCL, and say that we are at the dm_max_queued_msgs
+   limit, and that the first inode is already unlinked.	 In that case the
+   destroy event will block waiting to be queued, and the application thread
+   could happen to reference one of the other locked inodes.  Deadlock.
+*/
+
+void
+dm_evt_rele_tevp(
+	dm_tokevent_t	*tevp,
+	int		droprights)	/* non-zero, evt thread loses rights */
+{
+	dm_tokdata_t	*tdp;
+	unsigned long	lc;		/* lock cookie */
+
+	lc = mutex_spinlock(&tevp->te_lock);
+
+	/* If we are here without DM_TEF_FINAL set and with at least one
+	   application reference still remaining, then one of several
+	   possibilities is true:
+	   1. This is an asynchronous event which has been queued but has not
+	      yet been delivered, or which is in the process of being delivered.
+	   2. This is an unmount event (pseudo-asynchronous) yet to be
+	      delivered or in the process of being delivered.
+	   3. This event had DM_FLAGS_NDELAY specified, and the application
+	      has sent a dm_pending() reply for the event.
+	   4. This is a DM_EVENT_READ, DM_EVENT_WRITE, or DM_EVENT_TRUNCATE
+	      event and the user typed a Cntl-C.
+	   In all of these cases, the correct behavior is to leave the
+	   responsibility of releasing any rights to the application threads
+	   when they are done.
+	*/
+
+	if (tevp->te_app_ref > 0 && !(tevp->te_flags & DM_TEF_FINAL)) {
+		tevp->te_evt_ref--;
+		for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) {
+			if (tdp->td_flags & DM_TDF_EVTREF) {
+				tdp->td_flags &= ~DM_TDF_EVTREF;
+				if (tdp->td_vcount == 0) {
+					tdp->td_ip = NULL;
+				}
+			}
+		}
+		mutex_spinunlock(&tevp->te_lock, lc);
+		return;		/* not the last thread */
+	}
+
+	/* If the application reference count is non-zero here, that can only
+	   mean that dm_respond_event() has been called, but the application
+	   still has one or more threads in the kernel that haven't let go of
+	   the tevp.  In these cases, the event thread must wait until all
+	   application threads have given up their references, and their
+	   rights to handles within the event.
+	*/
+
+	while (tevp->te_app_ref) {
+		sv_wait(&tevp->te_evt_queue, 1, &tevp->te_lock, lc);
+		lc = mutex_spinlock(&tevp->te_lock);
+	}
+
+	/* This thread is the last active thread using the token/event.	 Reset
+	   the rights of any inode that was part of the original event back
+	   to their initial values before returning to the filesystem.	The
+	   exception is if the event failed (droprights is non-zero), in which
+	   case we chose to return to the filesystem with all rights released.
+	   Release the rights on any inode that was not part of the original
+	   event.  Give up all remaining application inode references
+	   regardless of whether or not the inode was part of the original
+	   event.
+	*/
+
+	mutex_spinunlock(&tevp->te_lock, lc);
+
+	while ((tdp = tevp->te_tdp) != NULL) {
+		tevp->te_tdp = tdp->td_next;
+		if ((tdp->td_flags & DM_TDF_ORIG) &&
+		    (tdp->td_flags & DM_TDF_EVTREF) &&
+		    (!droprights)) {
+			dm_change_right(tdp);
+		}
+		dm_put_tdp(tdp);
+		kmem_cache_free(dm_tokdata_cachep, tdp);
+	}
+	spinlock_destroy(&tevp->te_lock);
+	sv_destroy(&tevp->te_evt_queue);
+	sv_destroy(&tevp->te_app_queue);
+	kfree(tevp);
+}
+
+
+/* dm_obj_ref_hold() is just a fancy way to get an inode reference on an object
+   to hold it in kernel memory.
+*/
+
+int
+dm_obj_ref_hold(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_NULL, DM_FG_STHREAD, &tdp);
+
+	/* The tdp is single-threaded, so no mutex lock needed for update. */
+
+	if (error == 0) {
+		if (tdp->td_flags & DM_TDF_HOLD) {	/* if already held */
+			error = -EBUSY;
+		} else {
+			tdp->td_flags |= DM_TDF_HOLD;
+			tdp->td_vcount++;
+
+			fsys_vector = dm_fsys_vector(tdp->td_ip);
+			(void)fsys_vector->obj_ref_hold(tdp->td_ip);
+		}
+		dm_app_put_tdp(tdp);
+	}
+	return(error);
+}
+
+
+int
+dm_obj_ref_rele(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen)
+{
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_NULL, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp);
+
+	/* The tdp is single-threaded, so no mutex lock needed for update. */
+
+	if (error == 0) {
+		if (!(tdp->td_flags & DM_TDF_HOLD)) {	/* if not held */
+			error = -EACCES; /* use the DM_FG_MUSTEXIST errno */
+		} else {
+			tdp->td_flags &= ~DM_TDF_HOLD;
+			iput(tdp->td_ip);
+			tdp->td_vcount--;
+		}
+		dm_app_put_tdp(tdp);
+	}
+	return(error);
+}
+
+
+int
+dm_obj_ref_query_rvp(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	void		__user *hanp,
+	size_t		hlen,
+	int		*rvp)
+{
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_VNO,
+		DM_RIGHT_NULL, DM_FG_DONTADD|DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* If the request is valid but the handle just isn't present in the
+	   event or the hold flag isn't set, return zero, else return one.
+	*/
+
+	if (tdp) {
+		if (tdp->td_flags & DM_TDF_HOLD) {	/* if held */
+			*rvp = 1;
+		} else {
+			*rvp = 0;
+		}
+		dm_app_put_tdp(tdp);
+	} else {
+		*rvp = 0;
+	}
+	return(0);
+}
+
+
+int
+dm_downgrade_right(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_EXCL, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* Attempt the downgrade.  Filesystems which support rights but not
+	   the downgrading of rights will return ENOSYS.
+	*/
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->downgrade_right(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0));
+
+	/* The tdp is single-threaded, so no mutex lock needed for update. */
+
+	if (error == 0)
+		tdp->td_right = DM_RIGHT_SHARED;
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_query_right(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	dm_right_t	__user *rightp)
+{
+	dm_tokdata_t	*tdp;
+	dm_right_t	right;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_NULL, DM_FG_DONTADD|DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* Get the current right and copy it to the caller.  The tdp is
+	   single-threaded, so no mutex lock is needed.	 If the tdp is not in
+	   the event we are supposed to return DM_RIGHT_NULL in order to be
+	   compatible with Veritas.
+	*/
+
+	if (tdp) {
+		right = tdp->td_right;
+		dm_app_put_tdp(tdp);
+	} else {
+		right = DM_RIGHT_NULL;
+	}
+	if (copy_to_user(rightp, &right, sizeof(right)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+int
+dm_release_right(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_SHARED, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->release_right(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0));
+
+	/* The tdp is single-threaded, so no mutex lock needed for update. */
+
+	if (error == 0) {
+		tdp->td_right = DM_RIGHT_NULL;
+		if (tdp->td_flags & DM_TDF_RIGHT) {
+			tdp->td_flags &= ~DM_TDF_RIGHT;
+			iput(tdp->td_ip);
+			tdp->td_vcount--;
+		}
+	}
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_request_right(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token,
+	u_int		flags,
+	dm_right_t	right)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_NULL, DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->request_right(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0), flags, right);
+
+	/* The tdp is single-threaded, so no mutex lock is needed for update.
+
+	   If this is the first dm_request_right call for this inode, then we
+	   need to bump the inode reference count for two reasons.  First of
+	   all, it is supposed to be impossible for the file to disappear or
+	   for the filesystem to be unmounted while a right is held on a file;
+	   bumping the file's inode reference count ensures this.  Second, if
+	   rights are ever actually implemented, it will most likely be done
+	   without changes to the on-disk inode, which means that we can't let
+	   the inode become unreferenced while a right on it is held.
+	*/
+
+	if (error == 0) {
+		if (!(tdp->td_flags & DM_TDF_RIGHT)) {	/* if first call */
+			tdp->td_flags |= DM_TDF_RIGHT;
+			tdp->td_vcount++;
+			(void)fsys_vector->obj_ref_hold(tdp->td_ip);
+		}
+		tdp->td_right = right;
+	}
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
+
+
+int
+dm_upgrade_right(
+	dm_sessid_t	sid,
+	void		__user *hanp,
+	size_t		hlen,
+	dm_token_t	token)
+{
+	dm_fsys_vector_t *fsys_vector;
+	dm_tokdata_t	*tdp;
+	int		error;
+
+	error = dm_app_get_tdp_by_token(sid, hanp, hlen, token, DM_TDT_ANY,
+		DM_RIGHT_SHARED, DM_FG_MUSTEXIST|DM_FG_STHREAD, &tdp);
+	if (error != 0)
+		return(error);
+
+	/* If the object already has the DM_RIGHT_EXCL right, no need to
+	   attempt an upgrade.
+	*/
+
+	if (tdp->td_right == DM_RIGHT_EXCL) {
+		dm_app_put_tdp(tdp);
+		return(0);
+	}
+
+	/* Attempt the upgrade.	 Filesystems which support rights but not
+	   the upgrading of rights will return ENOSYS.
+	*/
+
+	fsys_vector = dm_fsys_vector(tdp->td_ip);
+	error = fsys_vector->upgrade_right(tdp->td_ip, tdp->td_right,
+		(tdp->td_type == DM_TDT_VFS ? DM_FSYS_OBJ : 0));
+
+	/* The tdp is single-threaded, so no mutex lock needed for update. */
+
+	if (error == 0)
+		tdp->td_right = DM_RIGHT_EXCL;
+
+	dm_app_put_tdp(tdp);
+	return(error);
+}
--- /dev/null
+++ b/fs/dmapi/dmapi_session.c
@@ -0,0 +1,1824 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/module.h>
+#endif
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+dm_session_t	*dm_sessions = NULL;	/* head of session list */
+u_int		dm_sessions_active = 0; /* # sessions currently active */
+dm_sessid_t	dm_next_sessid = 1;	/* next session ID to use */
+lock_t		dm_session_lock = SPIN_LOCK_UNLOCKED;/* lock for session list */
+
+dm_token_t	dm_next_token = 1;	/* next token ID to use */
+dm_sequence_t	dm_next_sequence = 1;	/* next sequence number to use */
+lock_t		dm_token_lock = SPIN_LOCK_UNLOCKED;/* dm_next_token/dm_next_sequence lock */
+
+int	dm_max_queued_msgs = 2048;	/* max # undelivered msgs/session */
+
+int	dm_hash_buckets = 1009;		/* prime -- number of buckets */
+
+#define DM_SHASH(sess,inodenum)	\
+		((sess)->sn_sesshash + do_mod((inodenum), dm_hash_buckets))
+
+
+#ifdef CONFIG_PROC_FS
+static int
+sessions_read_pfs(char *buffer, char **start, off_t offset,
+		 int count, int *eof, void *data)
+{
+	int len;
+	dm_session_t	*sessp = (dm_session_t*)data;
+
+#define CHKFULL if(len >= count) break;
+#define ADDBUF(a,b)	len += sprintf(buffer + len, a, b); CHKFULL;
+
+	len=0;
+	while(1){
+		ADDBUF("sessp=0x%p\n", sessp);
+		ADDBUF("sn_next=0x%p\n", sessp->sn_next);
+		ADDBUF("sn_sessid=%d\n", sessp->sn_sessid);
+		ADDBUF("sn_flags=%x\n", sessp->sn_flags);
+		ADDBUF("sn_qlock=%c\n", '?');
+		ADDBUF("sn_readerq=%c\n", '?');
+		ADDBUF("sn_writerq=%c\n", '?');
+		ADDBUF("sn_readercnt=%u\n", sessp->sn_readercnt);
+		ADDBUF("sn_writercnt=%u\n", sessp->sn_writercnt);
+
+		ADDBUF("sn_newq.eq_head=0x%p\n", sessp->sn_newq.eq_head);
+		ADDBUF("sn_newq.eq_tail=0x%p\n", sessp->sn_newq.eq_tail);
+		ADDBUF("sn_newq.eq_count=%d\n", sessp->sn_newq.eq_count);
+
+		ADDBUF("sn_delq.eq_head=0x%p\n", sessp->sn_delq.eq_head);
+		ADDBUF("sn_delq.eq_tail=0x%p\n", sessp->sn_delq.eq_tail);
+		ADDBUF("sn_delq.eq_count=%d\n", sessp->sn_delq.eq_count);
+
+		ADDBUF("sn_evt_writerq.eq_head=0x%p\n", sessp->sn_evt_writerq.eq_head);
+		ADDBUF("sn_evt_writerq.eq_tail=0x%p\n", sessp->sn_evt_writerq.eq_tail);
+		ADDBUF("sn_evt_writerq.eq_count=%d\n", sessp->sn_evt_writerq.eq_count);
+
+		ADDBUF("sn_info=\"%s\"\n", sessp->sn_info);
+
+		break;
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		*eof = 1;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
+
+	return len;
+}
+#endif
+
+
+/* Link a session to the end of the session list.  New sessions are always
+   added at the end of the list so that dm_enqueue_mount_event() doesn't
+   miss a session.  The caller must have obtained dm_session_lock before
+   calling this routine.
+*/
+
+static void
+link_session(
+	dm_session_t	*s)
+{
+	dm_session_t	*tmp;
+
+	if ((tmp = dm_sessions) == NULL) {
+		dm_sessions = s;
+	} else {
+		while (tmp->sn_next != NULL)
+			tmp = tmp->sn_next;
+		tmp->sn_next = s;
+	}
+	s->sn_next = NULL;
+	dm_sessions_active++;
+}
+
+
+/* Remove a session from the session list.  The caller must have obtained
+   dm_session_lock before calling this routine.	 unlink_session() should only
+   be used in situations where the session is known to be on the dm_sessions
+   list; otherwise it panics.
+*/
+
+static void
+unlink_session(
+	dm_session_t	*s)
+{
+	dm_session_t	*tmp;
+
+	if (dm_sessions == s) {
+		dm_sessions = dm_sessions->sn_next;
+	} else {
+		for (tmp = dm_sessions; tmp; tmp = tmp->sn_next) {
+			if (tmp->sn_next == s)
+				break;
+		}
+		if (tmp == NULL) {
+			panic("unlink_session: corrupt DMAPI session list, "
+				"dm_sessions %p, session %p\n",
+				dm_sessions, s);
+		}
+		tmp->sn_next = s->sn_next;
+	}
+	s->sn_next = NULL;
+	dm_sessions_active--;
+}
+
+
+/* Link an event to the end of an event queue.	The caller must have obtained
+   the session's sn_qlock before calling this routine.
+*/
+
+void
+dm_link_event(
+	dm_tokevent_t	*tevp,
+	dm_eventq_t	*queue)
+{
+	if (queue->eq_tail) {
+		queue->eq_tail->te_next = tevp;
+		queue->eq_tail = tevp;
+	} else {
+		queue->eq_head = queue->eq_tail = tevp;
+	}
+	tevp->te_next = NULL;
+	queue->eq_count++;
+}
+
+
+/* Remove an event from an event queue.	 The caller must have obtained the
+   session's sn_qlock before calling this routine.  dm_unlink_event() should
+   only be used in situations where the event is known to be on the queue;
+   otherwise it panics.
+*/
+
+void
+dm_unlink_event(
+	dm_tokevent_t	*tevp,
+	dm_eventq_t	*queue)
+{
+	dm_tokevent_t	*tmp;
+
+	if (queue->eq_head == tevp) {
+		queue->eq_head = tevp->te_next;
+		if (queue->eq_head == NULL)
+			queue->eq_tail = NULL;
+	} else {
+		tmp = queue->eq_head;
+		while (tmp && tmp->te_next != tevp)
+			tmp = tmp->te_next;
+		if (tmp == NULL) {
+			panic("dm_unlink_event: corrupt DMAPI queue %p, "
+				"tevp %p\n", queue, tevp);
+		}
+		tmp->te_next = tevp->te_next;
+		if (tmp->te_next == NULL)
+			queue->eq_tail = tmp;
+	}
+	tevp->te_next = NULL;
+	queue->eq_count--;
+}
+
+/* Link a regular file event to a hash bucket.	The caller must have obtained
+   the session's sn_qlock before calling this routine.
+   The tokevent must be for a regular file object--DM_TDT_REG.
+*/
+
+static void
+hash_event(
+	dm_session_t	*s,
+	dm_tokevent_t	*tevp)
+{
+	dm_sesshash_t	*sh;
+	dm_ino_t	ino;
+
+	if (s->sn_sesshash == NULL) {
+		s->sn_sesshash = kmalloc(dm_hash_buckets * sizeof(dm_sesshash_t), GFP_KERNEL);
+		if (s->sn_sesshash == NULL) {
+			printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+			return;
+		}
+		memset(s->sn_sesshash, 0, dm_hash_buckets * sizeof(dm_sesshash_t));
+	}
+
+	ino = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino;
+	sh = DM_SHASH(s, ino);
+
+#ifdef DM_SHASH_DEBUG
+	if (sh->h_next == NULL) {
+		s->sn_buckets_in_use++;
+		if (s->sn_buckets_in_use > s->sn_max_buckets_in_use)
+			s->sn_max_buckets_in_use++;
+	}
+	sh->maxlength++;
+	sh->curlength++;
+	sh->num_adds++;
+#endif
+
+	tevp->te_flags |= DM_TEF_HASHED;
+	tevp->te_hashnext = sh->h_next;
+	sh->h_next = tevp;
+}
+
+
+/* Remove a regular file event from a hash bucket.  The caller must have
+   obtained the session's sn_qlock before calling this routine.
+   The tokevent must be for a regular file object--DM_TDT_REG.
+*/
+
+static void
+unhash_event(
+	dm_session_t	*s,
+	dm_tokevent_t	*tevp)
+{
+	dm_sesshash_t	*sh;
+	dm_tokevent_t	*tmp;
+	dm_ino_t	ino;
+
+	if (s->sn_sesshash == NULL)
+		return;
+
+	ino = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino;
+	sh = DM_SHASH(s, ino);
+
+	if (sh->h_next == tevp) {
+		sh->h_next = tevp->te_hashnext; /* leap frog */
+	} else {
+		tmp = sh->h_next;
+		while (tmp->te_hashnext != tevp) {
+			tmp = tmp->te_hashnext;
+		}
+		tmp->te_hashnext = tevp->te_hashnext; /* leap frog */
+	}
+	tevp->te_hashnext = NULL;
+	tevp->te_flags &= ~DM_TEF_HASHED;
+
+#ifdef DM_SHASH_DEBUG
+	if (sh->h_next == NULL)
+		s->sn_buckets_in_use--;
+	sh->curlength--;
+	sh->num_dels++;
+#endif
+}
+
+
+/* Determine if this is a repeat event.	 The caller MUST be holding
+   the session lock.
+   The tokevent must be for a regular file object--DM_TDT_REG.
+   Returns:
+	0 == match not found
+	1 == match found
+*/
+
+static int
+repeated_event(
+	dm_session_t	*s,
+	dm_tokevent_t	*tevp)
+{
+	dm_sesshash_t	*sh;
+	dm_data_event_t *d_event1;
+	dm_data_event_t *d_event2;
+	dm_tokevent_t	*tevph;
+	dm_ino_t	ino1;
+	dm_ino_t	ino2;
+
+	if ((!s->sn_newq.eq_tail) && (!s->sn_delq.eq_tail)) {
+		return(0);
+	}
+	if (s->sn_sesshash == NULL) {
+		return(0);
+	}
+
+	ino1 = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino;
+	sh = DM_SHASH(s, ino1);
+
+	if (sh->h_next == NULL) {
+		/* bucket is empty, no match here */
+		return(0);
+	}
+
+	d_event1 = (dm_data_event_t *)((char *)&tevp->te_msg + tevp->te_msg.ev_data.vd_offset);
+	tevph = sh->h_next;
+	while (tevph) {
+		/* find something with the same event type and handle type */
+		if ((tevph->te_msg.ev_type == tevp->te_msg.ev_type) &&
+		    (tevph->te_tdp->td_type == tevp->te_tdp->td_type)) {
+
+			ino2 = (&tevp->te_tdp->td_handle.ha_fid)->dm_fid_ino;
+			d_event2 = (dm_data_event_t *)((char *)&tevph->te_msg + tevph->te_msg.ev_data.vd_offset);
+
+			/* If the two events are operating on the same file,
+			   and the same part of that file, then we have a
+			   match.
+			*/
+			if ((ino1 == ino2) &&
+			    (d_event2->de_offset == d_event1->de_offset) &&
+			    (d_event2->de_length == d_event1->de_length)) {
+				/* found a match */
+#ifdef DM_SHASH_DEBUG
+				sh->dup_hits++;
+#endif
+				return(1);
+			}
+		}
+		tevph = tevph->te_hashnext;
+	}
+
+	/* No match found */
+	return(0);
+}
+
+
+/* Return a pointer to a session given its session ID, or EINVAL if no session
+   has the session ID (per the DMAPI spec).  The caller must have obtained
+   dm_session_lock before calling this routine.
+*/
+
+static int
+dm_find_session(
+	dm_sessid_t	sid,
+	dm_session_t	**sessionpp)
+{
+	dm_session_t	*s;
+
+	for (s = dm_sessions; s; s = s->sn_next) {
+		if (s->sn_sessid == sid) {
+			*sessionpp = s;
+			return(0);
+		}
+	}
+	return(-EINVAL);
+}
+
+
+/* Return a pointer to a locked session given its session ID.  '*lcp' is
+   used to obtain the session's sn_qlock.  Caller is responsible for eventually
+   unlocking it.
+*/
+
+int
+dm_find_session_and_lock(
+	dm_sessid_t	sid,
+	dm_session_t	**sessionpp,
+	unsigned long	*lcp)		/* addr of returned lock cookie */
+{
+	int		error;
+
+	for (;;) {
+		*lcp = mutex_spinlock(&dm_session_lock);
+
+		if ((error = dm_find_session(sid, sessionpp)) != 0) {
+			mutex_spinunlock(&dm_session_lock, *lcp);
+			return(error);
+		}
+		if (spin_trylock(&(*sessionpp)->sn_qlock)) {
+			nested_spinunlock(&dm_session_lock);
+			return(0);	/* success */
+		}
+
+		/* If the second lock is not available, drop the first and
+		   start over.	This gives the CPU a chance to process any
+		   interrupts, and also allows processes which want a sn_qlock
+		   for a different session to proceed.
+		*/
+
+		mutex_spinunlock(&dm_session_lock, *lcp);
+	}
+}
+
+
+/* Return a pointer to the event on the specified session's sn_delq which
+   contains the given token.  The caller must have obtained the session's
+   sn_qlock before calling this routine.
+*/
+
+static int
+dm_find_msg(
+	dm_session_t	*s,
+	dm_token_t	token,
+	dm_tokevent_t	**tevpp)
+{
+	dm_tokevent_t	*tevp;
+
+	if (token <= DM_INVALID_TOKEN)
+		return(-EINVAL);
+
+	for (tevp = s->sn_delq.eq_head; tevp; tevp = tevp->te_next) {
+		if (tevp->te_msg.ev_token == token) {
+			*tevpp = tevp;
+			return(0);
+		}
+	}
+	return(-ESRCH);
+}
+
+
+/* Given a session ID and token, find the tevp on the specified session's
+   sn_delq which corresponds to that session ID/token pair.  If a match is
+   found, lock the tevp's te_lock and return a pointer to the tevp.
+   '*lcp' is used to obtain the tevp's te_lock.	 The caller is responsible
+   for eventually unlocking it.
+*/
+
+int
+dm_find_msg_and_lock(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	dm_tokevent_t	**tevpp,
+	unsigned long	*lcp)		/* address of returned lock cookie */
+{
+	dm_session_t	*s;
+	int		error;
+
+	if ((error = dm_find_session_and_lock(sid, &s, lcp)) != 0)
+		return(error);
+
+	if ((error = dm_find_msg(s, token, tevpp)) != 0) {
+		mutex_spinunlock(&s->sn_qlock, *lcp);
+		return(error);
+	}
+	nested_spinlock(&(*tevpp)->te_lock);
+	nested_spinunlock(&s->sn_qlock);
+	return(0);
+}
+
+
+/* Create a new session, or resume an old session if one is given. */
+
+int
+dm_create_session(
+	dm_sessid_t	old,
+	char		__user *info,
+	dm_sessid_t	__user *new)
+{
+	dm_session_t	*s;
+	dm_sessid_t	sid;
+	char		sessinfo[DM_SESSION_INFO_LEN];
+	size_t		len;
+	int		error;
+	unsigned long		lc;		/* lock cookie */
+
+	len = strnlen_user(info, DM_SESSION_INFO_LEN-1);
+	if (copy_from_user(sessinfo, info, len))
+		return(-EFAULT);
+	lc = mutex_spinlock(&dm_session_lock);
+	sid = dm_next_sessid++;
+	mutex_spinunlock(&dm_session_lock, lc);
+	if (copy_to_user(new, &sid, sizeof(sid)))
+		return(-EFAULT);
+
+	if (old == DM_NO_SESSION) {
+		s = kmem_cache_alloc(dm_session_cachep, GFP_KERNEL);
+		if (s == NULL) {
+			printk("%s/%d: kmem_cache_alloc(dm_session_cachep) returned NULL\n", __FUNCTION__, __LINE__);
+			return -ENOMEM;
+		}
+		memset(s, 0, sizeof(*s));
+
+		sv_init(&s->sn_readerq, SV_DEFAULT, "dmreadq");
+		sv_init(&s->sn_writerq, SV_DEFAULT, "dmwritq");
+		spinlock_init(&s->sn_qlock, "sn_qlock");
+	} else {
+		lc = mutex_spinlock(&dm_session_lock);
+		if ((error = dm_find_session(old, &s)) != 0) {
+			mutex_spinunlock(&dm_session_lock, lc);
+			return(error);
+		}
+		unlink_session(s);
+		mutex_spinunlock(&dm_session_lock, lc);
+#ifdef CONFIG_PROC_FS
+		{
+		char buf[100];
+		sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s);
+		remove_proc_entry(buf, NULL);
+		}
+#endif
+	}
+	memcpy(s->sn_info, sessinfo, len);
+	s->sn_info[len-1] = 0;		/* if not NULL, then now 'tis */
+	s->sn_sessid = sid;
+	lc = mutex_spinlock(&dm_session_lock);
+	link_session(s);
+	mutex_spinunlock(&dm_session_lock, lc);
+#ifdef CONFIG_PROC_FS
+	{
+	char buf[100];
+	struct proc_dir_entry *entry;
+
+	sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s);
+	entry = create_proc_read_entry(buf, 0, NULL, sessions_read_pfs, s);
+	}
+#endif
+	return(0);
+}
+
+
+int
+dm_destroy_session(
+	dm_sessid_t	sid)
+{
+	dm_session_t	*s;
+	int		error;
+	unsigned long		lc;		/* lock cookie */
+
+	/* The dm_session_lock must be held until the session is unlinked. */
+
+	lc = mutex_spinlock(&dm_session_lock);
+
+	if ((error = dm_find_session(sid, &s)) != 0) {
+		mutex_spinunlock(&dm_session_lock, lc);
+		return(error);
+	}
+	nested_spinlock(&s->sn_qlock);
+
+	/* The session exists.	Check to see if it is still in use.  If any
+	   messages still exist on the sn_newq or sn_delq, or if any processes
+	   are waiting for messages to arrive on the session, then the session
+	   must not be destroyed.
+	*/
+
+	if (s->sn_newq.eq_head || s->sn_readercnt || s->sn_delq.eq_head) {
+		nested_spinunlock(&s->sn_qlock);
+		mutex_spinunlock(&dm_session_lock, lc);
+		return(-EBUSY);
+	}
+
+	/* The session is not in use.  Dequeue it from the session chain. */
+
+	unlink_session(s);
+	nested_spinunlock(&s->sn_qlock);
+	mutex_spinunlock(&dm_session_lock, lc);
+
+#ifdef CONFIG_PROC_FS
+	{
+	char buf[100];
+	sprintf(buf, DMAPI_DBG_PROCFS "/sessions/0x%p", s);
+	remove_proc_entry(buf, NULL);
+	}
+#endif
+
+	/* Now clear the sessions's disposition registration, and then destroy
+	   the session structure.
+	*/
+
+	dm_clear_fsreg(s);
+
+	spinlock_destroy(&s->sn_qlock);
+	sv_destroy(&s->sn_readerq);
+	sv_destroy(&s->sn_writerq);
+	if (s->sn_sesshash)
+		kfree(s->sn_sesshash);
+	kmem_cache_free(dm_session_cachep, s);
+	return(0);
+}
+
+
+/*
+ *  Return a list of all active sessions.
+ */
+
+int
+dm_getall_sessions(
+	u_int		nelem,
+	dm_sessid_t	__user *sidp,
+	u_int		__user *nelemp)
+{
+	dm_session_t	*s;
+	u_int		sesscnt;
+	dm_sessid_t	*sesslist;
+	unsigned long		lc;		/* lock cookie */
+	int		error;
+	int		i;
+
+	/* Loop until we can get the right amount of temp space, being careful
+	   not to hold a mutex during the allocation.  Usually only one trip.
+	*/
+
+	for (;;) {
+		if ((sesscnt = dm_sessions_active) == 0) {
+			/*if (suword(nelemp, 0))*/
+			if (put_user(0, nelemp))
+				return(-EFAULT);
+			return(0);
+		}
+		sesslist = kmalloc(sesscnt * sizeof(*sidp), GFP_KERNEL);
+		if (sesslist == NULL) {
+			printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+			return -ENOMEM;
+		}
+
+		lc = mutex_spinlock(&dm_session_lock);
+		if (sesscnt == dm_sessions_active)
+			break;
+
+		mutex_spinunlock(&dm_session_lock, lc);
+		kfree(sesslist);
+	}
+
+	/* Make a temp copy of the data, then release the mutex. */
+
+	for (i = 0, s = dm_sessions; i < sesscnt; i++, s = s->sn_next)
+		sesslist[i] = s->sn_sessid;
+
+	mutex_spinunlock(&dm_session_lock, lc);
+
+	/* Now copy the data to the user. */
+
+	if(put_user(sesscnt, nelemp)) {
+		error = -EFAULT;
+	} else if (sesscnt > nelem) {
+		error = -E2BIG;
+	} else if (copy_to_user(sidp, sesslist, sesscnt * sizeof(*sidp))) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+	kfree(sesslist);
+	return(error);
+}
+
+
+/*
+ *  Return the descriptive string associated with a session.
+ */
+
+int
+dm_query_session(
+	dm_sessid_t	sid,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_session_t	*s;		/* pointer to session given by sid */
+	int		len;		/* length of session info string */
+	int		error;
+	char		sessinfo[DM_SESSION_INFO_LEN];
+	unsigned long	lc;		/* lock cookie */
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0)
+		return(error);
+
+	len = strlen(s->sn_info) + 1;	/* NULL terminated when created */
+	memcpy(sessinfo, s->sn_info, len);
+
+	mutex_spinunlock(&s->sn_qlock, lc);
+
+	/* Now that the mutex is released, copy the sessinfo to the user. */
+
+	if (put_user(len, rlenp)) {
+		error = -EFAULT;
+	} else if (len > buflen) {
+		error = -E2BIG;
+	} else if (copy_to_user(bufp, sessinfo, len)) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+	return(error);
+}
+
+
+/*
+ *  Return all of the previously delivered tokens (that is, their IDs)
+ *  for the given session.
+ */
+
+int
+dm_getall_tokens(
+	dm_sessid_t	sid,		/* session obtaining tokens from */
+	u_int		nelem,		/* size of tokenbufp */
+	dm_token_t	__user *tokenbufp,/* buffer to copy token IDs to */
+	u_int		__user *nelemp)	/* return number copied to tokenbufp */
+{
+	dm_session_t	*s;		/* pointer to session given by sid */
+	dm_tokevent_t	*tevp;		/* event message queue traversal */
+	unsigned long	lc;		/* lock cookie */
+	int		tokcnt;
+	dm_token_t	*toklist;
+	int		error;
+	int		i;
+
+	/* Loop until we can get the right amount of temp space, being careful
+	   not to hold a mutex during the allocation.  Usually only one trip.
+	*/
+
+	for (;;) {
+		if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0)
+			return(error);
+		tokcnt = s->sn_delq.eq_count;
+		mutex_spinunlock(&s->sn_qlock, lc);
+
+		if (tokcnt == 0) {
+			/*if (suword(nelemp, 0))*/
+			if (put_user(0, nelemp))
+				return(-EFAULT);
+			return(0);
+		}
+		toklist = kmalloc(tokcnt * sizeof(*tokenbufp), GFP_KERNEL);
+		if (toklist == NULL) {
+			printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+			return -ENOMEM;
+		}
+
+		if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0) {
+			kfree(toklist);
+			return(error);
+		}
+
+		if (tokcnt == s->sn_delq.eq_count)
+			break;
+
+		mutex_spinunlock(&s->sn_qlock, lc);
+		kfree(toklist);
+	}
+
+	/* Make a temp copy of the data, then release the mutex. */
+
+	tevp = s->sn_delq.eq_head;
+	for (i = 0; i < tokcnt; i++, tevp = tevp->te_next)
+		toklist[i] = tevp->te_msg.ev_token;
+
+	mutex_spinunlock(&s->sn_qlock, lc);
+
+	/* Now copy the data to the user. */
+
+	if (put_user(tokcnt, nelemp)) {
+		error = -EFAULT;
+	} else if (tokcnt > nelem) {
+		error = -E2BIG;
+	} else if (copy_to_user(tokenbufp,toklist,tokcnt*sizeof(*tokenbufp))) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+	kfree(toklist);
+	return(error);
+}
+
+
+/*
+ *  Return the message identified by token.
+ */
+
+int
+dm_find_eventmsg(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_tokevent_t	*tevp;		/* message identified by token */
+	int		msgsize;	/* size of message to copy out */
+	void		*msg;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	/* Because some of the events (dm_data_event_t in particular) contain
+	   __u64 fields, we need to make sure that the buffer provided by the
+	   caller is aligned such that he can read those fields successfully.
+	*/
+
+	if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0)
+		return(-EFAULT);
+
+	/* Allocate the right amount of temp space, being careful not to hold
+	   a mutex during the allocation.
+	*/
+
+	if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0)
+		return(error);
+	msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_msg);
+	mutex_spinunlock(&tevp->te_lock, lc);
+
+	msg = kmalloc(msgsize, GFP_KERNEL);
+	if (msg == NULL) {
+		printk("%s/%d: kmalloc returned NULL\n", __FUNCTION__, __LINE__);
+		return -ENOMEM;
+	}
+
+	if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0) {
+		kfree(msg);
+		return(error);
+	}
+
+	/* Make a temp copy of the data, then release the mutex. */
+
+	memcpy(msg, &tevp->te_msg, msgsize);
+	mutex_spinunlock(&tevp->te_lock, lc);
+
+	/* Now copy the data to the user. */
+
+	if (put_user(msgsize,rlenp)) {
+		error = -EFAULT;
+	} else if (msgsize > buflen) {		/* user buffer not big enough */
+		error = -E2BIG;
+	} else if (copy_to_user( bufp, msg, msgsize )) {
+		error = -EFAULT;
+	} else {
+		error = 0;
+	}
+	kfree(msg);
+	return(error);
+}
+
+
+int
+dm_move_event(
+	dm_sessid_t	srcsid,
+	dm_token_t	token,
+	dm_sessid_t	targetsid,
+	dm_token_t	__user *rtokenp)
+{
+	dm_session_t	*s1;
+	dm_session_t	*s2;
+	dm_tokevent_t	*tevp;
+	int		error;
+	unsigned long		lc;		/* lock cookie */
+	int		hash_it = 0;
+
+	lc = mutex_spinlock(&dm_session_lock);
+
+	if ((error = dm_find_session(srcsid, &s1)) != 0 ||
+	    (error = dm_find_session(targetsid, &s2)) != 0 ||
+	    (error = dm_find_msg(s1, token, &tevp)) != 0) {
+		mutex_spinunlock(&dm_session_lock, lc);
+		return(error);
+	}
+	dm_unlink_event(tevp, &s1->sn_delq);
+	if (tevp->te_flags & DM_TEF_HASHED) {
+		unhash_event(s1, tevp);
+		hash_it = 1;
+	}
+	dm_link_event(tevp, &s2->sn_delq);
+	if (hash_it)
+		hash_event(s2, tevp);
+	mutex_spinunlock(&dm_session_lock, lc);
+
+	if (copy_to_user(rtokenp, &token, sizeof(token)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* ARGSUSED */
+int
+dm_pending(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	dm_timestruct_t	__user *delay)		/* unused */
+{
+	dm_tokevent_t	*tevp;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	if ((error = dm_find_msg_and_lock(sid, token, &tevp, &lc)) != 0)
+		return(error);
+
+	tevp->te_flags |= DM_TEF_INTERMED;
+	if (tevp->te_evt_ref > 0)	/* if event generation threads exist */
+		sv_broadcast(&tevp->te_evt_queue);
+
+	mutex_spinunlock(&tevp->te_lock, lc);
+	return(0);
+}
+
+
+int
+dm_get_events(
+	dm_sessid_t	sid,
+	u_int		maxmsgs,
+	u_int		flags,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	dm_session_t	*s;		/* pointer to session given by sid */
+	dm_tokevent_t	*tevp;		/* next event message on queue */
+	int		error;
+	unsigned long	lc1;		/* first lock cookie */
+	unsigned long	lc2 = 0;	/* second lock cookie */
+	int		totalsize;
+	int		msgsize;
+	dm_eventmsg_t	__user *prevmsg;
+	int		prev_msgsize = 0;
+	u_int		msgcnt;
+
+	/* Because some of the events (dm_data_event_t in particular) contain
+	   __u64 fields, we need to make sure that the buffer provided by the
+	   caller is aligned such that he can read those fields successfully.
+	*/
+
+	if (((unsigned long)bufp & (sizeof(__u64) - 1)) != 0)
+		return(-EFAULT);
+
+	/* Find the indicated session and lock it. */
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc1)) != 0)
+		return(error);
+
+	/* Check for messages on sn_newq.  If there aren't any that haven't
+	   already been grabbed by another process, and if we are supposed to
+	   to wait until one shows up, then go to sleep interruptibly on the
+	   sn_readerq semaphore.  The session can't disappear out from under
+	   us as long as sn_readerq is non-zero.
+	*/
+
+	for (;;) {
+		int		rc;
+
+		for (tevp = s->sn_newq.eq_head; tevp; tevp = tevp->te_next) {
+			lc2 = mutex_spinlock(&tevp->te_lock);
+			if (!(tevp->te_flags & DM_TEF_LOCKED))
+				break;
+			mutex_spinunlock(&tevp->te_lock, lc2);
+		}
+		if (tevp)
+			break;		/* got one! */
+
+		if (!(flags & DM_EV_WAIT)) {
+			mutex_spinunlock(&s->sn_qlock, lc1);
+			return(-EAGAIN);
+		}
+		s->sn_readercnt++;
+
+		sv_wait_sig(&s->sn_readerq, 1, &s->sn_qlock, lc1);
+		rc = signal_pending(current);
+
+		lc1 = mutex_spinlock(&s->sn_qlock);
+		s->sn_readercnt--;
+		if (rc) {	/* if signal was received */
+			mutex_spinunlock(&s->sn_qlock, lc1);
+			return(-EINTR);
+		}
+	}
+
+	/* At least one message is available for delivery, and we have both the
+	   session lock and event lock.	 Mark the event so that it is not
+	   grabbed by other daemons, then drop both locks prior copying the
+	   data to the caller's buffer.	 Leaving the event on the queue in a
+	   marked state prevents both the session and the event from
+	   disappearing out from under us while we don't have the locks.
+	*/
+
+	tevp->te_flags |= DM_TEF_LOCKED;
+	mutex_spinunlock(&tevp->te_lock, lc2);	/* reverse cookie order */
+	mutex_spinunlock(&s->sn_qlock, lc1);
+
+	/* Continue to deliver messages until there are no more, the
+	   user's buffer becomes full, or we hit his maxmsgs limit.
+	*/
+
+	totalsize = 0;		/* total bytes transferred to the user */
+	prevmsg = NULL;
+	msgcnt = 0;
+
+	while (tevp) {
+		/* Compute the number of bytes to be moved, rounding up to an
+		   8-byte boundary so that any subsequent messages will also be
+		   aligned.
+		*/
+
+		msgsize = tevp->te_allocsize - offsetof(dm_tokevent_t, te_msg);
+		msgsize = (msgsize + sizeof(__u64) - 1) & ~(sizeof(__u64) - 1);
+		totalsize += msgsize;
+
+		/* If it fits, copy the message into the user's buffer and
+		   update his 'rlenp'.	Update the _link pointer for any
+		   previous message.
+		*/
+
+		if (totalsize > buflen) {	/* no more room */
+			error = -E2BIG;
+		} else if (put_user(totalsize, rlenp)) {
+			error = -EFAULT;
+		} else if (copy_to_user(bufp, &tevp->te_msg, msgsize)) {
+			error = -EFAULT;
+		} else if (prevmsg && put_user(prev_msgsize, &prevmsg->_link)) {
+			error = -EFAULT;
+		} else {
+			error = 0;
+		}
+
+		/* If an error occurred, just unmark the event and leave it on
+		   the queue for someone else.	Note that other daemons may
+		   have gone to sleep because this event was marked, so wake
+		   them up.  Also, if at least one message has already been
+		   delivered, then an error here is not really an error.
+		*/
+
+		lc1 = mutex_spinlock(&s->sn_qlock);
+		lc2 = mutex_spinlock(&tevp->te_lock);
+		tevp->te_flags &= ~DM_TEF_LOCKED;	/* drop the mark */
+
+		if (error) {
+			if (s->sn_readercnt)
+				sv_signal(&s->sn_readerq);
+
+			mutex_spinunlock(&tevp->te_lock, lc2);	/* rev. order */
+			mutex_spinunlock(&s->sn_qlock, lc1);
+			if (prevmsg)
+				return(0);
+			if (error == -E2BIG && put_user(totalsize,rlenp))
+				error = -EFAULT;
+			return(error);
+		}
+
+		/* The message was successfully delivered.  Unqueue it. */
+
+		dm_unlink_event(tevp, &s->sn_newq);
+
+		/* Wake up the first of any processes waiting for room on the
+		   sn_newq.
+		*/
+
+		if (s->sn_writercnt)
+			sv_signal(&s->sn_writerq);
+
+		/* If the message is synchronous, add it to the sn_delq while
+		   still holding the lock.  If it is asynchronous, free it.
+		*/
+
+		if (tevp->te_msg.ev_token != DM_INVALID_TOKEN) { /* synch */
+			dm_link_event(tevp, &s->sn_delq);
+			mutex_spinunlock(&tevp->te_lock, lc2);
+		} else {
+			tevp->te_flags |= DM_TEF_FINAL;
+			if (tevp->te_flags & DM_TEF_HASHED)
+				unhash_event(s, tevp);
+			mutex_spinunlock(&tevp->te_lock, lc2);
+			dm_put_tevp(tevp, NULL);/* can't cause destroy events */
+		}
+
+		/* Update our notion of where we are in the user's buffer.  If
+		   he doesn't want any more messages, then stop.
+		*/
+
+		prevmsg = (dm_eventmsg_t __user *)bufp;
+		prev_msgsize = msgsize;
+		bufp = (char __user *)bufp + msgsize;
+
+		msgcnt++;
+		if (maxmsgs && msgcnt >= maxmsgs) {
+			mutex_spinunlock(&s->sn_qlock, lc1);
+			break;
+		}
+
+		/* While still holding the sn_qlock,  see if any additional
+		   messages are available for delivery.
+		*/
+
+		for (tevp = s->sn_newq.eq_head; tevp; tevp = tevp->te_next) {
+			lc2 = mutex_spinlock(&tevp->te_lock);
+			if (!(tevp->te_flags & DM_TEF_LOCKED)) {
+				tevp->te_flags |= DM_TEF_LOCKED;
+				mutex_spinunlock(&tevp->te_lock, lc2);
+				break;
+			}
+			mutex_spinunlock(&tevp->te_lock, lc2);
+		}
+		mutex_spinunlock(&s->sn_qlock, lc1);
+	}
+	return(0);
+}
+
+
+/*
+ *  Remove an event message from the delivered queue, set the returned
+ *  error where the event generator wants it, and wake up the generator.
+ *  Also currently have the user side release any locks it holds...
+ */
+
+/* ARGSUSED */
+int
+dm_respond_event(
+	dm_sessid_t	sid,
+	dm_token_t	token,
+	dm_response_t	response,
+	int		reterror,
+	size_t		buflen,		/* unused */
+	void		__user *respbufp) /* unused */
+{
+	dm_session_t	*s;		/* pointer to session given by sid */
+	dm_tokevent_t	*tevp;		/* event message queue traversal */
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	/* Sanity check the input parameters. */
+
+	switch (response) {
+	case DM_RESP_CONTINUE:	/* continue must have reterror == 0 */
+		if (reterror != 0)
+			return(-EINVAL);
+		break;
+	case DM_RESP_ABORT:	/* abort must have errno set */
+		if (reterror <= 0)
+			return(-EINVAL);
+		break;
+	case DM_RESP_DONTCARE:
+		reterror = -1;	/* to distinguish DM_RESP_DONTCARE */
+		break;
+	default:
+		return(-EINVAL);
+	}
+
+	/* Hold session lock until the event is unqueued. */
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0)
+		return(error);
+
+	if ((error = dm_find_msg(s, token, &tevp)) != 0) {
+		mutex_spinunlock(&s->sn_qlock, lc);
+		return(error);
+	}
+	nested_spinlock(&tevp->te_lock);
+
+	if ((response == DM_RESP_DONTCARE) &&
+	    (tevp->te_msg.ev_type != DM_EVENT_MOUNT)) {
+		error = -EINVAL;
+		nested_spinunlock(&tevp->te_lock);
+		mutex_spinunlock(&s->sn_qlock, lc);
+	} else {
+		dm_unlink_event(tevp, &s->sn_delq);
+		if (tevp->te_flags & DM_TEF_HASHED)
+			unhash_event(s, tevp);
+		tevp->te_reply = -reterror; /* linux wants negative errno */
+		tevp->te_flags |= DM_TEF_FINAL;
+		if (tevp->te_evt_ref)
+			sv_broadcast(&tevp->te_evt_queue);
+		nested_spinunlock(&tevp->te_lock);
+		mutex_spinunlock(&s->sn_qlock, lc);
+		error = 0;
+
+		/* Absolutely no locks can be held when calling dm_put_tevp! */
+
+		dm_put_tevp(tevp, NULL);  /* this can generate destroy events */
+	}
+	return(error);
+}
+
+/* The caller must hold sn_qlock.
+   This will return the tokevent locked.
+ */
+static dm_tokevent_t *
+__find_match_event_no_waiters_locked(
+	dm_tokevent_t	*tevp1,
+	dm_eventq_t	*queue)
+{
+	dm_tokevent_t	*tevp2, *next_tevp;
+	dm_tokdata_t	*tdp1 = tevp1->te_tdp;
+	dm_tokdata_t	*tdp2;
+	dm_data_event_t *d_event1;
+	dm_data_event_t *d_event2;
+
+	d_event1 = (dm_data_event_t *)((char *)&tevp1->te_msg + tevp1->te_msg.ev_data.vd_offset);
+
+	for(tevp2 = queue->eq_head; tevp2; tevp2 = next_tevp) {
+		nested_spinlock(&tevp2->te_lock);
+		next_tevp = tevp2->te_next;
+
+		/* Just compare the first tdp's in each--there should
+		   be just one, if it's the match we want.
+		 */
+		tdp2 = tevp2->te_tdp;
+		if ((tevp2->te_msg.ev_type == tevp1->te_msg.ev_type) &&
+		    (tevp2->te_tdp->td_type == tevp1->te_tdp->td_type) &&
+		    (tevp2->te_evt_ref == 0) && (tdp2->td_next == NULL) &&
+		    (memcmp(&tdp1->td_handle, &tdp2->td_handle,
+			   sizeof(dm_handle_t)) == 0)) {
+
+			d_event2 = (dm_data_event_t *)((char *)&tevp2->te_msg + tevp2->te_msg.ev_data.vd_offset);
+
+
+			if ((d_event2->de_offset == d_event1->de_offset) &&
+			    (d_event2->de_length == d_event1->de_length)) {
+				/* Match -- return it locked */
+				return tevp2;
+			}
+		}
+		nested_spinunlock(&tevp2->te_lock);
+	}
+	return NULL;
+}
+
+/* The caller must hold the sn_qlock.
+   The returned tokevent will be locked with nested_spinlock.
+ */
+static dm_tokevent_t *
+find_match_event_no_waiters_locked(
+	dm_session_t	*s,
+	dm_tokevent_t	*tevp)
+{
+	dm_tokevent_t	*tevp2;
+
+	if ((!s->sn_newq.eq_tail) && (!s->sn_delq.eq_tail))
+		return NULL;
+	if (!tevp->te_tdp)
+		return NULL;
+	if (tevp->te_tdp->td_next) {
+		/* If it has multiple tdp's then don't bother trying to
+		   find a match.
+		 */
+		return NULL;
+	}
+	tevp2 = __find_match_event_no_waiters_locked(tevp, &s->sn_newq);
+	if (tevp2 == NULL)
+		tevp2 = __find_match_event_no_waiters_locked(tevp, &s->sn_delq);
+	/* returns a locked tokevent */
+	return tevp2;
+}
+
+
+
+/* Queue the filled in event message pointed to by tevp on the session s, and
+   (if a synchronous event) wait for the reply from the DMAPI application.
+   The caller MUST be holding the session lock before calling this routine!
+   The session lock is always released upon exit.
+   Returns:
+	 -1 == don't care
+	  0 == success (or async event)
+	> 0 == errno describing reason for failure
+*/
+
+static int
+dm_enqueue(
+	dm_session_t	*s,
+	unsigned long	lc,		/* input lock cookie */
+	dm_tokevent_t	**tevpp,		/* in/out parameter */
+	int		sync,
+	int		flags,
+	int		interruptable)
+{
+	int		is_unmount = 0;
+	int		is_hashable = 0;
+	int		reply;
+	dm_tokevent_t	*tevp = *tevpp;
+
+	/* If the caller isn't planning to stick around for the result
+	   and this request is identical to one that is already on the
+	   queues then just give the caller an EAGAIN.	Release the
+	   session lock before returning.
+
+	   We look only at NDELAY requests with an event type of READ,
+	   WRITE, or TRUNCATE on objects that are regular files.
+	*/
+
+	if ((flags & DM_FLAGS_NDELAY) && DM_EVENT_RDWRTRUNC(tevp) &&
+	    (tevp->te_tdp->td_type == DM_TDT_REG)) {
+		if (repeated_event(s, tevp)) {
+			mutex_spinunlock(&s->sn_qlock, lc);
+			return -EAGAIN;
+		}
+		is_hashable = 1;
+	}
+
+	/* If the caller is a sync event then look for a matching sync
+	   event.  If there is a match and it doesn't currently have
+	   event threads waiting on it, then we will drop our own
+	   tokevent and jump on the matching event.
+	 */
+	if (((flags & DM_FLAGS_NDELAY) == 0) && DM_EVENT_RDWRTRUNC(tevp) &&
+	    (tevp->te_tdp->td_type == DM_TDT_REG)) {
+		dm_tokevent_t	*tevp2;
+		if ((tevp2 = find_match_event_no_waiters_locked(s, tevp))) {
+			ASSERT(tevp2->te_evt_ref == 0);
+			tevp2->te_evt_ref++;
+			nested_spinunlock(&tevp2->te_lock);
+			nested_spinlock(&tevp->te_lock);
+			tevp->te_evt_ref--;
+			nested_spinunlock(&tevp->te_lock);
+			mutex_spinunlock(&s->sn_qlock, lc);
+			/* All locks have been released */
+			dm_evt_rele_tevp(tevp, 1);
+			*tevpp = tevp = tevp2;
+			goto wait_on_tevp;
+		}
+	}
+
+	if (tevp->te_msg.ev_type == DM_EVENT_UNMOUNT)
+		is_unmount = 1;
+
+	/* Check for room on sn_newq.  If there is no room for new messages,
+	   then go to sleep on the sn_writerq semaphore.  The
+	   session cannot disappear out from under us as long as sn_writercnt
+	   is non-zero.
+	*/
+
+	while (s->sn_newq.eq_count >= dm_max_queued_msgs) {	/* no room */
+		s->sn_writercnt++;
+		dm_link_event(tevp, &s->sn_evt_writerq);
+		if (interruptable) {
+			sv_wait_sig(&s->sn_writerq, 1, &s->sn_qlock, lc);
+			if (signal_pending(current)) {
+				s->sn_writercnt--;
+				return -EINTR;
+			}
+		} else {
+			sv_wait(&s->sn_writerq, 1, &s->sn_qlock, lc);
+		}
+		lc = mutex_spinlock(&s->sn_qlock);
+		s->sn_writercnt--;
+		dm_unlink_event(tevp, &s->sn_evt_writerq);
+#ifdef HAVE_DM_QUEUE_FLUSH
+		/* We hold the sn_qlock, from here to after we get into
+		 * the sn_newq.  Any thread going through
+		 * dm_release_threads() looking for us is already past us
+		 * and has set the DM_TEF_FLUSH flag for us or is blocked on
+		 * sn_qlock and will find us in sn_newq after we release
+		 * the sn_qlock.
+		 * We check for dop->flushing anyway, in case the
+		 * dm_release_threads() already completed before we
+		 * could enter dmapi.
+		 */
+		if (!sync) {
+			/* async events are forced into the newq */
+			break;
+		}
+		if (tevp->te_flags & DM_TEF_FLUSH) {
+			mutex_spinunlock(&s->sn_qlock, lc);
+			return tevp->te_reply;
+		}
+		else {
+			struct filesystem_dmapi_operations *dops;
+			dm_tokdata_t	*tdp;
+			int		errno = 0;
+
+			nested_spinlock(&tevp->te_lock);
+			for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) {
+				if (tdp->td_ip) {
+					dops = dm_fsys_ops(tdp->td_ip->i_sb);
+					ASSERT(dops);
+					if (dops->flushing)
+						errno = dops->flushing(tdp->td_ip);
+					if (errno) {
+						nested_spinunlock(&tevp->te_lock);
+						mutex_spinunlock(&s->sn_qlock, lc);
+						return errno;
+					}
+				}
+			}
+			nested_spinunlock(&tevp->te_lock);
+		}
+#endif /* HAVE_DM_QUEUE_FLUSH */
+	}
+
+	/* Assign a sequence number and token to the event and bump the
+	   application reference count by one.	We don't need 'te_lock' here
+	   because this thread is still the only thread that can see the event.
+	*/
+
+	nested_spinlock(&dm_token_lock);
+	tevp->te_msg.ev_sequence = dm_next_sequence++;
+	if (sync) {
+		tevp->te_msg.ev_token = dm_next_token++;
+	} else {
+		tevp->te_msg.ev_token = DM_INVALID_TOKEN;
+	}
+	nested_spinunlock(&dm_token_lock);
+
+	tevp->te_app_ref++;
+
+	/* Room exists on the sn_newq queue, so add this request.  If the
+	   queue was previously empty, wake up the first of any processes
+	   that are waiting for an event.
+	*/
+
+	dm_link_event(tevp, &s->sn_newq);
+	if (is_hashable)
+		hash_event(s, tevp);
+
+	if (s->sn_readercnt)
+		sv_signal(&s->sn_readerq);
+
+	mutex_spinunlock(&s->sn_qlock, lc);
+
+	/* Now that the message is queued, processes issuing asynchronous
+	   events or DM_EVENT_UNMOUNT events are ready to continue.
+	*/
+
+	if (!sync || is_unmount)
+		return 0;
+
+	/* Synchronous requests wait until a final reply is received.  If the
+	   caller supplied the DM_FLAGS_NDELAY flag, the process will return
+	   EAGAIN if dm_pending() sets DM_TEF_INTERMED.	 We also let users
+	   Cntl-C out of a read, write, and truncate requests.
+	*/
+
+wait_on_tevp:
+	lc = mutex_spinlock(&tevp->te_lock);
+
+	while (!(tevp->te_flags & DM_TEF_FINAL)) {
+		if ((tevp->te_flags & DM_TEF_INTERMED) &&
+		    (flags & DM_FLAGS_NDELAY)) {
+			mutex_spinunlock(&tevp->te_lock, lc);
+			return -EAGAIN;
+		}
+		if (tevp->te_msg.ev_type == DM_EVENT_READ ||
+		    tevp->te_msg.ev_type == DM_EVENT_WRITE ||
+		    tevp->te_msg.ev_type == DM_EVENT_TRUNCATE) {
+			sv_wait_sig(&tevp->te_evt_queue, 1, &tevp->te_lock, lc);
+			if (signal_pending(current)){
+				return -EINTR;
+			}
+		} else {
+			sv_wait(&tevp->te_evt_queue, 1, &tevp->te_lock, lc);
+		}
+		lc = mutex_spinlock(&tevp->te_lock);
+#ifdef HAVE_DM_QUEUE_FLUSH
+		/* Did we pop out because of queue flushing? */
+		if (tevp->te_flags & DM_TEF_FLUSH) {
+			mutex_spinunlock(&tevp->te_lock, lc);
+			return tevp->te_reply;
+		}
+#endif /* HAVE_DM_QUEUE_FLUSH */
+	}
+
+	/* Return both the tevp and the reply which was stored in the tevp by
+	   dm_respond_event.  The tevp structure has already been removed from
+	   the reply queue by this point in dm_respond_event().
+	*/
+
+	reply = tevp->te_reply;
+	mutex_spinunlock(&tevp->te_lock, lc);
+	return reply;
+}
+
+
+/* The filesystem is guaranteed to stay mounted while this event is
+   outstanding.
+*/
+
+int
+dm_enqueue_normal_event(
+	struct super_block *sb,
+	dm_tokevent_t	**tevpp,
+	int		flags)
+{
+	dm_session_t	*s;
+	int		error;
+	int		sync;
+	unsigned long	lc;		/* lock cookie */
+
+	switch ((*tevpp)->te_msg.ev_type) {
+	case DM_EVENT_READ:
+	case DM_EVENT_WRITE:
+	case DM_EVENT_TRUNCATE:
+	case DM_EVENT_PREUNMOUNT:
+	case DM_EVENT_UNMOUNT:
+	case DM_EVENT_NOSPACE:
+	case DM_EVENT_CREATE:
+	case DM_EVENT_REMOVE:
+	case DM_EVENT_RENAME:
+	case DM_EVENT_SYMLINK:
+	case DM_EVENT_LINK:
+	case DM_EVENT_DEBUT:		/* not currently supported */
+		sync = 1;
+		break;
+
+	case DM_EVENT_DESTROY:
+	case DM_EVENT_POSTCREATE:
+	case DM_EVENT_POSTREMOVE:
+	case DM_EVENT_POSTRENAME:
+	case DM_EVENT_POSTSYMLINK:
+	case DM_EVENT_POSTLINK:
+	case DM_EVENT_ATTRIBUTE:
+	case DM_EVENT_CLOSE:		/* not currently supported */
+	case DM_EVENT_CANCEL:		/* not currently supported */
+		sync = 0;
+		break;
+
+	default:
+		return(-EIO);		/* garbage event number */
+	}
+
+	/* Wait until a session selects disposition for the event.  The session
+	   is locked upon return from dm_waitfor_disp_session().
+	*/
+
+	if ((error = dm_waitfor_disp_session(sb, *tevpp, &s, &lc)) != 0)
+		return(error);
+
+	return(dm_enqueue(s, lc, tevpp, sync, flags, 0));
+}
+
+
+/* Traverse the session list checking for sessions with the WANTMOUNT flag
+   set.	 When one is found, send it the message.  Possible responses to the
+   message are one of DONTCARE, CONTINUE, or ABORT.  The action taken in each
+   case is:
+	DONTCARE (-1)  - Send the event to the next session with WANTMOUNT set
+	CONTINUE ( 0) - Proceed with the mount, errno zero.
+	ABORT	 (>0) - Fail the mount, return the returned errno.
+
+   The mount request is sent to sessions in ascending session ID order.
+   Since the session list can change dramatically while this process is
+   sleeping in dm_enqueue(), this routine must use session IDs rather than
+   session pointers when keeping track of where it is in the list.  Since
+   new sessions are always added at the end of the queue, and have increasing
+   session ID values, we don't have to worry about missing any session.
+*/
+
+int
+dm_enqueue_mount_event(
+	struct super_block *sb,
+	dm_tokevent_t	*tevp)
+{
+	dm_session_t	*s;
+	dm_sessid_t	sid;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	/* Make the mounting filesystem visible to other DMAPI calls. */
+
+	if ((error = dm_add_fsys_entry(sb, tevp)) != 0){
+		return(error);
+	}
+
+	/* Walk through the session list presenting the mount event to each
+	   session that is interested until a session accepts or rejects it,
+	   or until all sessions ignore it.
+	*/
+
+	for (sid = DM_NO_SESSION, error = 1; error > 0; sid = s->sn_sessid) {
+
+		lc = mutex_spinlock(&dm_session_lock);
+		for (s = dm_sessions; s; s = s->sn_next) {
+			if (s->sn_sessid > sid && s->sn_flags & DM_SN_WANTMOUNT) {
+				nested_spinlock(&s->sn_qlock);
+				nested_spinunlock(&dm_session_lock);
+				break;
+			}
+		}
+		if (s == NULL) {
+			mutex_spinunlock(&dm_session_lock, lc);
+			break;		/* noone wants it; proceed with mount */
+		}
+		error = dm_enqueue(s, lc, &tevp, 1, 0, 0);
+	}
+
+	/* If the mount will be allowed to complete, then update the fsrp entry
+	   accordingly.	 If the mount is to be aborted, remove the fsrp entry.
+	*/
+
+	if (error >= 0) {
+		dm_change_fsys_entry(sb, DM_STATE_MOUNTED);
+		error = 0;
+	} else {
+		dm_remove_fsys_entry(sb);
+	}
+	return(error);
+}
+
+int
+dm_enqueue_sendmsg_event(
+	dm_sessid_t	targetsid,
+	dm_tokevent_t	*tevp,
+	int		sync)
+{
+	dm_session_t	*s;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	if ((error = dm_find_session_and_lock(targetsid, &s, &lc)) != 0)
+		return(error);
+
+	return(dm_enqueue(s, lc, &tevp, sync, 0, 1));
+}
+
+
+dm_token_t
+dm_enqueue_user_event(
+	dm_sessid_t	sid,
+	dm_tokevent_t	*tevp,
+	dm_token_t	*tokenp)
+{
+	dm_session_t	*s;
+	int		error;
+	unsigned long	lc;		/* lock cookie */
+
+	/* Atomically find and lock the session whose session id is 'sid'. */
+
+	if ((error = dm_find_session_and_lock(sid, &s, &lc)) != 0)
+		return(error);
+
+	/* Assign a sequence number and token to the event, bump the
+	   application reference count by one, and decrement the event
+	   count because the caller gives up all ownership of the event.
+	   We don't need 'te_lock' here because this thread is still the
+	   only thread that can see the event.
+	*/
+
+	nested_spinlock(&dm_token_lock);
+	tevp->te_msg.ev_sequence = dm_next_sequence++;
+	*tokenp = tevp->te_msg.ev_token = dm_next_token++;
+	nested_spinunlock(&dm_token_lock);
+
+	tevp->te_flags &= ~(DM_TEF_INTERMED|DM_TEF_FINAL);
+	tevp->te_app_ref++;
+	tevp->te_evt_ref--;
+
+	/* Add the request to the tail of the sn_delq.	Now it's visible. */
+
+	dm_link_event(tevp, &s->sn_delq);
+	mutex_spinunlock(&s->sn_qlock, lc);
+
+	return(0);
+}
+
+#ifdef HAVE_DM_QUEUE_FLUSH
+/* If inode is non-null, find any tdp referencing that inode and flush the
+ * thread waiting on that inode and set DM_TEF_FLUSH for that tokevent.
+ * Otherwise, if inode is null, find any tdp referencing the specified fsid
+ * and flush that thread and set DM_TEF_FLUSH for that tokevent.
+ */
+static int
+dm_flush_events(
+	dm_session_t	*s,
+	dm_fsid_t	*fsidp,
+	struct inode	*inode, /* may be null */
+	dm_eventq_t	*queue,
+	int		is_writerq,
+	int		errno)
+{
+	dm_tokevent_t	*tevp, *next_tevp;
+	dm_tokdata_t	*tdp;
+	int		found_events = 0;
+
+	ASSERT(fsidp);
+	for (tevp = queue->eq_head; tevp; tevp = next_tevp) {
+		nested_spinlock(&tevp->te_lock);
+		next_tevp = tevp->te_next;
+
+		for (tdp = tevp->te_tdp; tdp; tdp = tdp->td_next) {
+			if( inode ) {
+				if( tdp->td_ip == inode ) {
+					break;
+				}
+			}
+			else if(memcmp(fsidp, &tdp->td_handle.ha_fsid, sizeof(*fsidp)) == 0) {
+				break;
+			}
+		}
+
+		if (tdp != NULL) {
+			/* found a handle reference in this event */
+			++found_events;
+			tevp->te_flags |= DM_TEF_FLUSH;
+
+			/* Set the reply value, unless dm_get_events is
+			   already on this one.
+			 */
+			if (! (tevp->te_flags & DM_TEF_LOCKED))
+				tevp->te_reply = errno;
+
+			/* If it is on the sn_evt_writerq or is being
+			   used by dm_get_events then we're done with it.
+			*/
+			if (is_writerq || (tevp->te_flags & DM_TEF_LOCKED)) {
+				nested_spinunlock(&tevp->te_lock);
+				continue;
+			}
+
+			/* If there is a thread waiting on a synchronous
+			   event then be like dm_respond_event.
+			*/
+
+			if ((tevp->te_evt_ref) &&
+			    (tevp->te_msg.ev_token != DM_INVALID_TOKEN)) {
+
+				tevp->te_flags |= DM_TEF_FINAL;
+				dm_unlink_event(tevp, queue);
+				if (tevp->te_flags & DM_TEF_HASHED)
+					unhash_event(s, tevp);
+				sv_broadcast(&tevp->te_evt_queue);
+				nested_spinunlock(&tevp->te_lock);
+				dm_put_tevp(tevp, NULL);
+				continue;
+			}
+		}
+		nested_spinunlock(&tevp->te_lock);
+	}
+
+	return(found_events);
+}
+
+
+/* If inode is non-null then find any threads that have a reference to that
+ * inode and flush them with the specified errno.
+ * Otherwise,if inode is null, then find any threads that have a reference
+ * to that sb and flush them with the specified errno.
+ * We look for these threads in each session's sn_evt_writerq, sn_newq,
+ * and sn_delq.
+ */
+int
+dm_release_threads(
+	struct super_block	*sb,
+	struct inode		*inode, /* may be null */
+	int			errno)
+{
+	dm_sessid_t	sid;
+	dm_session_t	*s;
+	unsigned long	lc;
+	u_int		sesscnt;
+	dm_sessid_t	*sidlist;
+	int		i;
+	int		found_events = 0;
+	dm_fsid_t	fsid;
+	struct filesystem_dmapi_operations *dops;
+
+	ASSERT(sb);
+	dops = dm_fsys_ops(sb);
+	ASSERT(dops);
+	dops->get_fsid(sb, &fsid);
+	dm_release_disp_threads(&fsid, inode, errno);
+
+	/* Loop until we can get the right amount of temp space, being careful
+	   not to hold a mutex during the allocation.  Usually only one trip.
+	*/
+
+	for (;;) {
+		lc = mutex_spinlock(&dm_session_lock);
+		sesscnt = dm_sessions_active;
+		mutex_spinunlock(&dm_session_lock, lc);
+
+		if (sesscnt == 0)
+			return 0;
+
+		sidlist = kmalloc(sesscnt * sizeof(sid), GFP_KERNEL);
+
+		lc = mutex_spinlock(&dm_session_lock);
+		if (sesscnt == dm_sessions_active)
+			break;
+
+		mutex_spinunlock(&dm_session_lock, lc);
+		kfree(sidlist);
+	}
+
+	for (i = 0, s = dm_sessions; i < sesscnt; i++, s = s->sn_next)
+		sidlist[i] = s->sn_sessid;
+
+	mutex_spinunlock(&dm_session_lock, lc);
+
+
+	for (i = 0; i < sesscnt; i++) {
+		sid = sidlist[i];
+		if( dm_find_session_and_lock( sid, &s, &lc ) == 0 ){
+			found_events = dm_flush_events( s, &fsid, inode,
+							&s->sn_evt_writerq, 1,
+							errno );
+			if (found_events)
+				sv_broadcast(&s->sn_writerq);
+
+			dm_flush_events(s, &fsid, inode, &s->sn_newq, 0, errno);
+			dm_flush_events(s, &fsid, inode, &s->sn_delq, 0, errno);
+
+			mutex_spinunlock( &s->sn_qlock, lc );
+		}
+	}
+	kfree(sidlist);
+
+	return 0;
+}
+#endif /* HAVE_DM_QUEUE_FLUSH */
--- /dev/null
+++ b/fs/dmapi/dmapi_sysent.c
@@ -0,0 +1,801 @@
+/*
+ * Copyright (c) 2000-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+/* Data Migration API (DMAPI)
+ */
+
+
+/* We're using MISC_MAJOR / MISC_DYNAMIC_MINOR. */
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+#include "dmapi.h"
+#include "dmapi_kern.h"
+#include "dmapi_private.h"
+
+struct kmem_cache	*dm_fsreg_cachep = NULL;
+struct kmem_cache	*dm_tokdata_cachep = NULL;
+struct kmem_cache	*dm_session_cachep = NULL;
+struct kmem_cache	*dm_fsys_map_cachep = NULL;
+struct kmem_cache	*dm_fsys_vptr_cachep = NULL;
+
+static int
+dmapi_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	    unsigned long arg)
+{
+	sys_dmapi_args_t kargs;
+	sys_dmapi_args_t *uap = &kargs;
+	int error = 0;
+	int rvp = -ENOSYS;
+	int use_rvp = 0;
+
+	if (!capable(CAP_MKNOD))
+		return -EPERM;
+
+	if( copy_from_user( &kargs, (sys_dmapi_args_t __user *)arg,
+			   sizeof(sys_dmapi_args_t) ) )
+		return -EFAULT;
+
+	unlock_kernel();
+
+	switch (cmd) {
+	case DM_CLEAR_INHERIT:
+		error = dm_clear_inherit(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrname_t __user *) DM_Parg(uap,5));/* attrnamep */
+		break;
+	case DM_CREATE_BY_HANDLE:
+		error = dm_create_by_handle(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* dirhanp */
+				(size_t)	DM_Uarg(uap,3), /* dirhlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(void __user *)	DM_Parg(uap,5), /* hanp */
+				(size_t)	DM_Uarg(uap,6), /* hlen */
+				(char __user *)	DM_Parg(uap,7));/* cname */
+		break;
+	case DM_CREATE_SESSION:
+		error = dm_create_session(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* oldsid */
+				(char __user *)	DM_Parg(uap,2), /* sessinfop */
+				(dm_sessid_t __user *) DM_Parg(uap,3));/* newsidp */
+		break;
+	case DM_CREATE_USEREVENT:
+		error = dm_create_userevent(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(size_t)	DM_Uarg(uap,2), /* msglen */
+				(void __user *)	DM_Parg(uap,3), /* msgdatap */
+				(dm_token_t __user *) DM_Parg(uap,4));/* tokenp */
+		break;
+	case DM_DESTROY_SESSION:
+		error = dm_destroy_session(
+				(dm_sessid_t)	DM_Uarg(uap,1));/* sid */
+		break;
+	case DM_DOWNGRADE_RIGHT:
+		error = dm_downgrade_right(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4));/* token */
+		break;
+	case DM_FD_TO_HANDLE:
+		error = dm_fd_to_hdl(
+				(int)		DM_Uarg(uap,1), /* fd */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t __user *) DM_Parg(uap,3));/* hlenp */
+		break;
+	case DM_FIND_EVENTMSG:
+		error = dm_find_eventmsg(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(size_t)	DM_Uarg(uap,3), /* buflen */
+				(void __user *)	DM_Parg(uap,4), /* bufp */
+				(size_t __user *) DM_Parg(uap,5));/* rlenp */
+		break;
+	case DM_GET_ALLOCINFO:
+		use_rvp = 1;
+		error = dm_get_allocinfo_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_off_t __user *) DM_Parg(uap,5), /* offp */
+				(u_int)		DM_Uarg(uap,6), /* nelem */
+				(dm_extent_t __user *) DM_Parg(uap,7), /* extentp */
+				(u_int __user *) DM_Parg(uap,8), /* nelemp */
+						&rvp);
+		break;
+	case DM_GET_BULKALL:
+		use_rvp = 1;
+		error = dm_get_bulkall_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* mask */
+				(dm_attrname_t __user *) DM_Parg(uap,6),/* attrnamep */
+				(dm_attrloc_t __user *) DM_Parg(uap,7),/* locp */
+				(size_t)	DM_Uarg(uap,8), /* buflen */
+				(void __user *)	DM_Parg(uap,9), /* bufp */
+				(size_t __user *) DM_Parg(uap,10),/* rlenp */
+						&rvp);
+		break;
+	case DM_GET_BULKATTR:
+		use_rvp = 1;
+		error = dm_get_bulkattr_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* mask */
+				(dm_attrloc_t __user *)DM_Parg(uap,6), /* locp */
+				(size_t)	DM_Uarg(uap,7), /* buflen */
+				(void __user *)	DM_Parg(uap,8), /* bufp */
+				(size_t __user *) DM_Parg(uap,9), /* rlenp */
+						&rvp);
+		break;
+	case DM_GET_CONFIG:
+		error = dm_get_config(
+				(void __user *)	DM_Parg(uap,1), /* hanp */
+				(size_t)	DM_Uarg(uap,2), /* hlen */
+				(dm_config_t)	DM_Uarg(uap,3), /* flagname */
+				(dm_size_t __user *)DM_Parg(uap,4));/* retvalp */
+		break;
+	case DM_GET_CONFIG_EVENTS:
+		error = dm_get_config_events(
+				(void __user *)	DM_Parg(uap,1), /* hanp */
+				(size_t)	DM_Uarg(uap,2), /* hlen */
+				(u_int)		DM_Uarg(uap,3), /* nelem */
+				(dm_eventset_t __user *) DM_Parg(uap,4),/* eventsetp */
+				(u_int __user *) DM_Parg(uap,5));/* nelemp */
+		break;
+	case DM_GET_DIOINFO:
+		error = dm_get_dioinfo(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_dioinfo_t __user *)DM_Parg(uap,5));/* diop */
+		break;
+	case DM_GET_DIRATTRS:
+		use_rvp = 1;
+		error = dm_get_dirattrs_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* mask */
+				(dm_attrloc_t __user *)DM_Parg(uap,6), /* locp */
+				(size_t)	DM_Uarg(uap,7), /* buflen */
+				(void __user *)	DM_Parg(uap,8), /* bufp */
+				(size_t __user *) DM_Parg(uap,9), /* rlenp */
+						&rvp);
+		break;
+	case DM_GET_DMATTR:
+		error = dm_get_dmattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */
+				(size_t)	DM_Uarg(uap,6), /* buflen */
+				(void __user *)	DM_Parg(uap,7), /* bufp */
+				(size_t __user *)	DM_Parg(uap,8));/* rlenp */
+
+		break;
+	case DM_GET_EVENTLIST:
+		error = dm_get_eventlist(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* nelem */
+				(dm_eventset_t __user *) DM_Parg(uap,6),/* eventsetp */
+				(u_int __user *) DM_Parg(uap,7));/* nelemp */
+		break;
+	case DM_GET_EVENTS:
+		error = dm_get_events(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(u_int)		DM_Uarg(uap,2), /* maxmsgs */
+				(u_int)		DM_Uarg(uap,3), /* flags */
+				(size_t)	DM_Uarg(uap,4), /* buflen */
+				(void __user *)	DM_Parg(uap,5), /* bufp */
+				(size_t __user *) DM_Parg(uap,6));/* rlenp */
+		break;
+	case DM_GET_FILEATTR:
+		error = dm_get_fileattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* mask */
+				(dm_stat_t __user *) DM_Parg(uap,6));/* statp */
+		break;
+	case DM_GET_MOUNTINFO:
+		error = dm_get_mountinfo(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(size_t)	DM_Uarg(uap,5), /* buflen */
+				(void __user *)	DM_Parg(uap,6), /* bufp */
+				(size_t __user *) DM_Parg(uap,7));/* rlenp */
+		break;
+	case DM_GET_REGION:
+		error = dm_get_region(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* nelem */
+				(dm_region_t __user *) DM_Parg(uap,6), /* regbufp */
+				(u_int __user *) DM_Parg(uap,7));/* nelemp */
+		break;
+	case DM_GETALL_DISP:
+		error = dm_getall_disp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(size_t)	DM_Uarg(uap,2), /* buflen */
+				(void __user *)	DM_Parg(uap,3), /* bufp */
+				(size_t __user *) DM_Parg(uap,4));/* rlenp */
+		break;
+	case DM_GETALL_DMATTR:
+		error = dm_getall_dmattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(size_t)	DM_Uarg(uap,5), /* buflen */
+				(void __user *)	DM_Parg(uap,6), /* bufp */
+				(size_t __user *) DM_Parg(uap,7));/* rlenp */
+		break;
+	case DM_GETALL_INHERIT:
+		error = dm_getall_inherit(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* nelem */
+				(dm_inherit_t __user *)DM_Parg(uap,6), /* inheritbufp*/
+				(u_int __user *) DM_Parg(uap,7));/* nelemp */
+		break;
+	case DM_GETALL_SESSIONS:
+		error = dm_getall_sessions(
+				(u_int)		DM_Uarg(uap,1), /* nelem */
+				(dm_sessid_t __user *) DM_Parg(uap,2), /* sidbufp */
+				(u_int __user *) DM_Parg(uap,3));/* nelemp */
+		break;
+	case DM_GETALL_TOKENS:
+		error = dm_getall_tokens(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(u_int)		DM_Uarg(uap,2), /* nelem */
+				(dm_token_t __user *) DM_Parg(uap,3), /* tokenbufp */
+				(u_int __user *) DM_Parg(uap,4));/* nelemp */
+		break;
+	case DM_INIT_ATTRLOC:
+		error = dm_init_attrloc(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrloc_t __user *) DM_Parg(uap,5));/* locp */
+		break;
+	case DM_MKDIR_BY_HANDLE:
+		error = dm_mkdir_by_handle(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* dirhanp */
+				(size_t)	DM_Uarg(uap,3), /* dirhlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(void __user *)	DM_Parg(uap,5), /* hanp */
+				(size_t)	DM_Uarg(uap,6), /* hlen */
+				(char __user *)	DM_Parg(uap,7));/* cname */
+		break;
+	case DM_MOVE_EVENT:
+		error = dm_move_event(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* srcsid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(dm_sessid_t)	DM_Uarg(uap,3), /* targetsid */
+				(dm_token_t __user *) DM_Parg(uap,4));/* rtokenp */
+		break;
+	case DM_OBJ_REF_HOLD:
+		error = dm_obj_ref_hold(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(void __user *)	DM_Parg(uap,3), /* hanp */
+				(size_t)	DM_Uarg(uap,4));/* hlen */
+		break;
+	case DM_OBJ_REF_QUERY:
+		use_rvp = 1;
+		error = dm_obj_ref_query_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(void __user *)	DM_Parg(uap,3), /* hanp */
+				(size_t)	DM_Uarg(uap,4), /* hlen */
+						&rvp);
+		break;
+	case DM_OBJ_REF_RELE:
+		error = dm_obj_ref_rele(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(void __user *)	DM_Parg(uap,3), /* hanp */
+				(size_t)	DM_Uarg(uap,4));/* hlen */
+		break;
+	case DM_PATH_TO_FSHANDLE:
+		error = dm_path_to_fshdl(
+				(char __user *)	DM_Parg(uap,1), /* path */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t __user *) DM_Parg(uap,3));/* hlenp */
+		break;
+	case DM_PATH_TO_HANDLE:
+		error = dm_path_to_hdl(
+				(char __user *)	DM_Parg(uap,1), /* path */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t __user *) DM_Parg(uap,3));/* hlenp */
+		break;
+	case DM_PENDING:
+		error = dm_pending(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(dm_timestruct_t __user *) DM_Parg(uap,3));/* delay */
+		break;
+	case DM_PROBE_HOLE:
+		error = dm_probe_hole(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_off_t)	DM_Uarg(uap,5), /* off */
+				(dm_size_t)	DM_Uarg(uap,6), /* len */
+				(dm_off_t __user *) DM_Parg(uap,7), /* roffp */
+				(dm_size_t __user *) DM_Parg(uap,8));/* rlenp */
+		break;
+	case DM_PUNCH_HOLE:
+		error = dm_punch_hole(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_off_t)	DM_Uarg(uap,5), /* off */
+				(dm_size_t)	DM_Uarg(uap,6));/* len */
+		break;
+	case DM_QUERY_RIGHT:
+		error = dm_query_right(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_right_t __user *) DM_Parg(uap,5));/* rightp */
+		break;
+	case DM_QUERY_SESSION:
+		error = dm_query_session(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(size_t)	DM_Uarg(uap,2), /* buflen */
+				(void __user *)	DM_Parg(uap,3), /* bufp */
+				(size_t __user *) DM_Parg(uap,4));/* rlenp */
+		break;
+	case DM_READ_INVIS:
+		use_rvp = 1;
+		error = dm_read_invis_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_off_t)	DM_Uarg(uap,5), /* off */
+				(dm_size_t)	DM_Uarg(uap,6), /* len */
+				(void __user *)	DM_Parg(uap,7), /* bufp */
+						&rvp);
+		break;
+	case DM_RELEASE_RIGHT:
+		error = dm_release_right(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4));/* token */
+		break;
+	case DM_REMOVE_DMATTR:
+		error = dm_remove_dmattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(int)		DM_Uarg(uap,5), /* setdtime */
+				(dm_attrname_t __user *) DM_Parg(uap,6));/* attrnamep */
+		break;
+	case DM_REQUEST_RIGHT:
+		error = dm_request_right(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* flags */
+				(dm_right_t)	DM_Uarg(uap,6));/* right */
+		break;
+	case DM_RESPOND_EVENT:
+		error = dm_respond_event(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(dm_token_t)	DM_Uarg(uap,2), /* token */
+				(dm_response_t) DM_Uarg(uap,3), /* response */
+				(int)		DM_Uarg(uap,4), /* reterror */
+				(size_t)	DM_Uarg(uap,5), /* buflen */
+				(void __user *)	DM_Parg(uap,6));/* respbufp */
+		break;
+	case DM_SEND_MSG:
+		error = dm_send_msg(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* targetsid */
+				(dm_msgtype_t)	DM_Uarg(uap,2), /* msgtype */
+				(size_t)	DM_Uarg(uap,3), /* buflen */
+				(void __user *)	DM_Parg(uap,4));/* bufp */
+		break;
+	case DM_SET_DISP:
+		error = dm_set_disp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_eventset_t __user *) DM_Parg(uap,5),/* eventsetp */
+				(u_int)		DM_Uarg(uap,6));/* maxevent */
+		break;
+	case DM_SET_DMATTR:
+		error = dm_set_dmattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */
+				(int)		DM_Uarg(uap,6), /* setdtime */
+				(size_t)	DM_Uarg(uap,7), /* buflen */
+				(void __user *)	DM_Parg(uap,8));/* bufp */
+		break;
+	case DM_SET_EVENTLIST:
+		error = dm_set_eventlist(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_eventset_t __user *) DM_Parg(uap,5),/* eventsetp */
+				(u_int)		DM_Uarg(uap,6));/* maxevent */
+		break;
+	case DM_SET_FILEATTR:
+		error = dm_set_fileattr(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* mask */
+				(dm_fileattr_t __user *)DM_Parg(uap,6));/* attrp */
+		break;
+	case DM_SET_INHERIT:
+		error = dm_set_inherit(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrname_t __user *)DM_Parg(uap,5),/* attrnamep */
+				(mode_t)	DM_Uarg(uap,6));/* mode */
+		break;
+	case DM_SET_REGION:
+		error = dm_set_region(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(u_int)		DM_Uarg(uap,5), /* nelem */
+				(dm_region_t __user *) DM_Parg(uap,6), /* regbufp */
+				(dm_boolean_t __user *) DM_Parg(uap,7));/* exactflagp */
+		break;
+	case DM_SET_RETURN_ON_DESTROY:
+		error = dm_set_return_on_destroy(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(dm_attrname_t __user *) DM_Parg(uap,5),/* attrnamep */
+				(dm_boolean_t)	DM_Uarg(uap,6));/* enable */
+		break;
+	case DM_SYMLINK_BY_HANDLE:
+		error = dm_symlink_by_handle(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* dirhanp */
+				(size_t)	DM_Uarg(uap,3), /* dirhlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(void __user *)	DM_Parg(uap,5), /* hanp */
+				(size_t)	DM_Uarg(uap,6), /* hlen */
+				(char __user *)	DM_Parg(uap,7), /* cname */
+				(char __user *)	DM_Parg(uap,8));/* path */
+		break;
+	case DM_SYNC_BY_HANDLE:
+		error = dm_sync_by_handle(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4));/* token */
+		break;
+	case DM_UPGRADE_RIGHT:
+		error = dm_upgrade_right(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4));/* token */
+		break;
+	case DM_WRITE_INVIS:
+		use_rvp = 1;
+		error = dm_write_invis_rvp(
+				(dm_sessid_t)	DM_Uarg(uap,1), /* sid */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(dm_token_t)	DM_Uarg(uap,4), /* token */
+				(int)		DM_Uarg(uap,5), /* flags */
+				(dm_off_t)	DM_Uarg(uap,6), /* off */
+				(dm_size_t)	DM_Uarg(uap,7), /* len */
+				(void __user *)	DM_Parg(uap,8), /* bufp */
+						&rvp);
+		break;
+	case DM_OPEN_BY_HANDLE:
+		use_rvp = 1;
+		error = dm_open_by_handle_rvp(
+				(unsigned int)	DM_Uarg(uap,1), /* fd */
+				(void __user *)	DM_Parg(uap,2), /* hanp */
+				(size_t)	DM_Uarg(uap,3), /* hlen */
+				(int)		DM_Uarg(uap,4), /* flags */
+						&rvp);
+		break;
+	default:
+		error = -ENOSYS;
+		break;
+	}
+
+	lock_kernel();
+
+	/* If it was an *_rvp() function, then
+		if error==0, return |rvp|
+	*/
+	if( use_rvp && (error == 0) )
+		return rvp;
+	else
+		return error;
+}
+
+
+
+static int
+dmapi_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+static int
+dmapi_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+/* say hello, and let me know the device is hooked up */
+static ssize_t
+dmapi_dump(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+	char tmp[50];
+	int len;
+	if( *ppos == 0 ){
+		len = sprintf( tmp, "# " DM_VER_STR_CONTENTS "\n" );
+		if( copy_to_user(buf, tmp, len) )
+			return -EFAULT;
+		*ppos += 1;
+		return len;
+	}
+	return 0;
+}
+
+static struct file_operations dmapi_fops = {
+	.open		= dmapi_open,
+	.ioctl		= dmapi_ioctl,
+	.read		= dmapi_dump,
+	.release	= dmapi_release
+};
+
+static struct miscdevice dmapi_dev = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= "dmapi",
+	.fops		= &dmapi_fops
+};
+
+
+
+#ifdef CONFIG_PROC_FS
+static int
+dmapi_summary(char *buffer, char **start, off_t offset,
+		 int count, int *eof, void *data)
+{
+	int len;
+
+	extern u_int dm_sessions_active;
+	extern dm_sessid_t dm_next_sessid;
+	extern dm_token_t dm_next_token;
+	extern dm_sequence_t dm_next_sequence;
+	extern int dm_fsys_cnt;
+
+#define CHKFULL if(len >= count) break;
+#define ADDBUF(a,b)	len += sprintf(buffer + len, a, b); CHKFULL;
+
+	len=0;
+	while(1){
+		ADDBUF("dm_sessions_active=%u\n", dm_sessions_active);
+		ADDBUF("dm_next_sessid=%d\n", (int)dm_next_sessid);
+		ADDBUF("dm_next_token=%d\n", (int)dm_next_token);
+		ADDBUF("dm_next_sequence=%u\n", (u_int)dm_next_sequence);
+		ADDBUF("dm_fsys_cnt=%d\n", dm_fsys_cnt);
+
+		break;
+	}
+
+	if (offset >= len) {
+		*start = buffer;
+		*eof = 1;
+		return 0;
+	}
+	*start = buffer + offset;
+	if ((len -= offset) > count)
+		return count;
+	*eof = 1;
+
+	return len;
+}
+#endif
+
+
+static void __init
+dmapi_init_procfs(int dmapi_minor)
+{
+#ifdef CONFIG_PROC_FS
+	struct proc_dir_entry *entry;
+
+	if ((entry = proc_mkdir( DMAPI_DBG_PROCFS, NULL)) == NULL )
+		return;
+	entry->mode = S_IFDIR | S_IRUSR | S_IXUSR;
+
+	if ((entry = proc_mkdir( DMAPI_DBG_PROCFS "/fsreg", NULL)) == NULL )
+		return;
+
+	if ((entry = proc_mkdir( DMAPI_DBG_PROCFS "/sessions", NULL)) == NULL )
+		return;
+
+	entry = create_proc_read_entry( DMAPI_DBG_PROCFS "/summary",
+			0, NULL, dmapi_summary, NULL);
+#endif
+}
+
+#if 0
+static void __exit
+dmapi_cleanup_procfs(void)
+{
+#ifdef CONFIG_PROC_FS
+	remove_proc_entry( DMAPI_DBG_PROCFS "/summary", NULL);
+	remove_proc_entry( DMAPI_DBG_PROCFS "/fsreg", NULL);
+	remove_proc_entry( DMAPI_DBG_PROCFS "/sessions", NULL);
+	remove_proc_entry( DMAPI_DBG_PROCFS, NULL);
+#endif
+}
+#endif
+
+int __init dmapi_init(void)
+{
+	int ret;
+
+	dm_tokdata_cachep = kmem_cache_create("dm_tokdata",
+				sizeof(struct dm_tokdata), 0, 0, NULL);
+	if (dm_tokdata_cachep == NULL)
+		goto out;
+
+	dm_fsreg_cachep = kmem_cache_create("dm_fsreg",
+				sizeof(struct dm_fsreg), 0, 0, NULL);
+	if (dm_fsreg_cachep == NULL)
+		goto out_free_tokdata_cachep;
+
+	dm_session_cachep = kmem_cache_create("dm_session",
+				sizeof(struct dm_session), 0, 0, NULL);
+	if (dm_session_cachep == NULL)
+		goto out_free_fsreg_cachep;
+
+	dm_fsys_map_cachep = kmem_cache_create("dm_fsys_map",
+				sizeof(dm_vector_map_t), 0, 0, NULL);
+	if (dm_fsys_map_cachep == NULL)
+		goto out_free_session_cachep;
+	dm_fsys_vptr_cachep = kmem_cache_create("dm_fsys_vptr",
+				sizeof(dm_fsys_vector_t), 0, 0, NULL);
+	if (dm_fsys_vptr_cachep == NULL)
+		goto out_free_fsys_map_cachep;
+
+	ret = misc_register(&dmapi_dev);
+	if (ret) {
+		printk(KERN_ERR "dmapi_init: misc_register returned %d\n", ret);
+		goto out_free_fsys_vptr_cachep;
+	}
+
+	dmapi_init_procfs(dmapi_dev.minor);
+	return 0;
+
+ out_free_fsys_vptr_cachep:
+	kmem_cache_destroy(dm_fsys_vptr_cachep);
+ out_free_fsys_map_cachep:
+	kmem_cache_destroy(dm_fsys_map_cachep);
+ out_free_session_cachep:
+	kmem_cache_destroy(dm_session_cachep);
+ out_free_fsreg_cachep:
+	kmem_cache_destroy(dm_fsreg_cachep);
+ out_free_tokdata_cachep:
+	kmem_cache_destroy(dm_tokdata_cachep);
+ out:
+	return -ENOMEM;
+}
+
+#if 0
+void __exit dmapi_uninit(void)
+{
+	misc_deregister(&dmapi_dev);
+	dmapi_cleanup_procfs();
+	kmem_cache_destroy(dm_tokdata_cachep);
+	kmem_cache_destroy(dm_fsreg_cachep);
+	kmem_cache_destroy(dm_session_cachep);
+	kmem_cache_destroy(dm_fsys_map_cachep);
+	kmem_cache_destroy(dm_fsys_vptr_cachep);
+}
+#endif
+
+module_init(dmapi_init);
+/*module_exit(dmapi_uninit);*/ /* Some other day */
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("SGI Data Migration Subsystem");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(dm_send_mount_event);
+EXPORT_SYMBOL(dm_send_namesp_event);
+EXPORT_SYMBOL(dm_send_unmount_event);
+EXPORT_SYMBOL(dm_send_data_event);
+EXPORT_SYMBOL(dm_send_destroy_event);
+EXPORT_SYMBOL(dm_ip_to_handle);
+EXPORT_SYMBOL(dmapi_register);
+EXPORT_SYMBOL(dmapi_unregister);
+EXPORT_SYMBOL(dmapi_registered);
+EXPORT_SYMBOL(dm_release_threads);
--- /dev/null
+++ b/fs/dmapi/sv.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program 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.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+#ifndef __DMAPI_SV_H__
+#define __DMAPI_SV_H__
+
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+/*
+ * Synchronisation variables.
+ *
+ * (Parameters "pri", "svf" and "rts" are not implemented)
+ */
+
+typedef struct sv_s {
+	wait_queue_head_t waiters;
+} sv_t;
+
+#define SV_FIFO		0x0		/* sv_t is FIFO type */
+#define SV_LIFO		0x2		/* sv_t is LIFO type */
+#define SV_PRIO		0x4		/* sv_t is PRIO type */
+#define SV_KEYED	0x6		/* sv_t is KEYED type */
+#define SV_DEFAULT      SV_FIFO
+
+
+static inline void _sv_wait(sv_t *sv, spinlock_t *lock, int state,
+			     unsigned long timeout)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue_exclusive(&sv->waiters, &wait);
+	__set_current_state(state);
+	spin_unlock(lock);
+
+	schedule_timeout(timeout);
+
+	remove_wait_queue(&sv->waiters, &wait);
+}
+
+#define init_sv(sv,type,name,flag) \
+	init_waitqueue_head(&(sv)->waiters)
+#define sv_init(sv,flag,name) \
+	init_waitqueue_head(&(sv)->waiters)
+#define sv_destroy(sv) \
+	/*NOTHING*/
+#define sv_wait(sv, pri, lock, s) \
+	_sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
+#define sv_wait_sig(sv, pri, lock, s)   \
+	_sv_wait(sv, lock, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT)
+#define sv_timedwait(sv, pri, lock, s, svf, ts, rts) \
+	_sv_wait(sv, lock, TASK_UNINTERRUPTIBLE, timespec_to_jiffies(ts))
+#define sv_timedwait_sig(sv, pri, lock, s, svf, ts, rts) \
+	_sv_wait(sv, lock, TASK_INTERRUPTIBLE, timespec_to_jiffies(ts))
+#define sv_signal(sv) \
+	wake_up(&(sv)->waiters)
+#define sv_broadcast(sv) \
+	wake_up_all(&(sv)->waiters)
+
+#endif /* __DMAPI_SV_H__ */
