Date: Thu, 09 Oct 2008 17:11:31 +1100 From: Donald Douwsma Subject: DMAPI Source Patch-mainline: ? References: bnc#450658 Acked-by: Jan Kara --- 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 +#endif +#include + +#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 +#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 +#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 + +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 +#include "sv.h" + +#include /* preempt needs this */ +#include + +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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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 +#include +#include +#ifdef CONFIG_PROC_FS +#include +#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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 +#include +#include + +/* + * 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__ */