2005-04-17 02:20:36 +04:00
/*
* Mostly platform independent upcall operations to Venus :
* - - upcalls
* - - upcall routines
*
* Linux 2.0 version
* Copyright ( C ) 1996 Peter J . Braam < braam @ maths . ox . ac . uk > ,
* Michael Callahan < callahan @ maths . ox . ac . uk >
*
* Redone for Linux 2.1
* Copyright ( C ) 1997 Carnegie Mellon University
*
* Carnegie Mellon University encourages users of this code to contribute
* improvements to the Coda project . Contact Peter Braam < coda @ cs . cmu . edu > .
*/
# include <asm/system.h>
# include <linux/signal.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/time.h>
# include <linux/fs.h>
# include <linux/file.h>
# include <linux/stat.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <asm/uaccess.h>
# include <linux/vmalloc.h>
# include <linux/vfs.h>
# include <linux/coda.h>
# include <linux/coda_linux.h>
# include <linux/coda_psdev.h>
# include <linux/coda_fs_i.h>
# include <linux/coda_cache.h>
# include <linux/coda_proc.h>
# define upc_alloc() kmalloc(sizeof(struct upc_req), GFP_KERNEL)
# define upc_free(r) kfree(r)
static int coda_upcall ( struct coda_sb_info * mntinfo , int inSize , int * outSize ,
union inputArgs * buffer ) ;
static void * alloc_upcall ( int opcode , int size )
{
union inputArgs * inp ;
CODA_ALLOC ( inp , union inputArgs * , size ) ;
if ( ! inp )
return ERR_PTR ( - ENOMEM ) ;
inp - > ih . opcode = opcode ;
inp - > ih . pid = current - > pid ;
inp - > ih . pgid = process_group ( current ) ;
# ifdef CONFIG_CODA_FS_OLD_API
memset ( & inp - > ih . cred , 0 , sizeof ( struct coda_cred ) ) ;
inp - > ih . cred . cr_fsuid = current - > fsuid ;
# else
inp - > ih . uid = current - > fsuid ;
# endif
return ( void * ) inp ;
}
# define UPARG(op)\
do { \
inp = ( union inputArgs * ) alloc_upcall ( op , insize ) ; \
if ( IS_ERR ( inp ) ) { return PTR_ERR ( inp ) ; } \
outp = ( union outputArgs * ) ( inp ) ; \
outsize = insize ; \
} while ( 0 )
# define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
# define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
# define SIZE(tag) max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
/* the upcalls */
int venus_rootfid ( struct super_block * sb , struct CodaFid * fidp )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( root ) ;
UPARG ( CODA_ROOT ) ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
if ( error ) {
printk ( " coda_get_rootfid: error %d \n " , error ) ;
} else {
* fidp = outp - > coda_root . VFid ;
}
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_getattr ( struct super_block * sb , struct CodaFid * fid ,
struct coda_vattr * attr )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( getattr ) ;
UPARG ( CODA_GETATTR ) ;
inp - > coda_getattr . VFid = * fid ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
* attr = outp - > coda_getattr . attr ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_setattr ( struct super_block * sb , struct CodaFid * fid ,
struct coda_vattr * vattr )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( setattr ) ;
UPARG ( CODA_SETATTR ) ;
inp - > coda_setattr . VFid = * fid ;
inp - > coda_setattr . attr = * vattr ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_lookup ( struct super_block * sb , struct CodaFid * fid ,
const char * name , int length , int * type ,
struct CodaFid * resfid )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset ;
offset = INSIZE ( lookup ) ;
insize = max_t ( unsigned int , offset + length + 1 , OUTSIZE ( lookup ) ) ;
UPARG ( CODA_LOOKUP ) ;
inp - > coda_lookup . VFid = * fid ;
inp - > coda_lookup . name = offset ;
inp - > coda_lookup . flags = CLU_CASE_SENSITIVE ;
/* send Venus a null terminated string */
memcpy ( ( char * ) ( inp ) + offset , name , length ) ;
* ( ( char * ) inp + offset + length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
* resfid = outp - > coda_lookup . VFid ;
* type = outp - > coda_lookup . vtype ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_store ( struct super_block * sb , struct CodaFid * fid , int flags ,
vuid_t uid )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
# ifdef CONFIG_CODA_FS_OLD_API
struct coda_cred cred = { 0 , } ;
cred . cr_fsuid = uid ;
# endif
insize = SIZE ( store ) ;
UPARG ( CODA_STORE ) ;
# ifdef CONFIG_CODA_FS_OLD_API
memcpy ( & ( inp - > ih . cred ) , & cred , sizeof ( cred ) ) ;
# else
inp - > ih . uid = uid ;
# endif
inp - > coda_store . VFid = * fid ;
inp - > coda_store . flags = flags ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_release ( struct super_block * sb , struct CodaFid * fid , int flags )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( release ) ;
UPARG ( CODA_RELEASE ) ;
inp - > coda_release . VFid = * fid ;
inp - > coda_release . flags = flags ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_close ( struct super_block * sb , struct CodaFid * fid , int flags ,
vuid_t uid )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
# ifdef CONFIG_CODA_FS_OLD_API
struct coda_cred cred = { 0 , } ;
cred . cr_fsuid = uid ;
# endif
insize = SIZE ( release ) ;
UPARG ( CODA_CLOSE ) ;
# ifdef CONFIG_CODA_FS_OLD_API
memcpy ( & ( inp - > ih . cred ) , & cred , sizeof ( cred ) ) ;
# else
inp - > ih . uid = uid ;
# endif
inp - > coda_close . VFid = * fid ;
inp - > coda_close . flags = flags ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_open ( struct super_block * sb , struct CodaFid * fid ,
int flags , struct file * * fh )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( open_by_fd ) ;
UPARG ( CODA_OPEN_BY_FD ) ;
inp - > coda_open . VFid = * fid ;
inp - > coda_open . flags = flags ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
* fh = outp - > coda_open_by_fd . fh ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_mkdir ( struct super_block * sb , struct CodaFid * dirfid ,
const char * name , int length ,
struct CodaFid * newfid , struct coda_vattr * attrs )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset ;
offset = INSIZE ( mkdir ) ;
insize = max_t ( unsigned int , offset + length + 1 , OUTSIZE ( mkdir ) ) ;
UPARG ( CODA_MKDIR ) ;
inp - > coda_mkdir . VFid = * dirfid ;
inp - > coda_mkdir . attr = * attrs ;
inp - > coda_mkdir . name = offset ;
/* Venus must get null terminated string */
memcpy ( ( char * ) ( inp ) + offset , name , length ) ;
* ( ( char * ) inp + offset + length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
* attrs = outp - > coda_mkdir . attr ;
* newfid = outp - > coda_mkdir . VFid ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_rename ( struct super_block * sb , struct CodaFid * old_fid ,
struct CodaFid * new_fid , size_t old_length ,
size_t new_length , const char * old_name ,
const char * new_name )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset , s ;
offset = INSIZE ( rename ) ;
insize = max_t ( unsigned int , offset + new_length + old_length + 8 ,
OUTSIZE ( rename ) ) ;
UPARG ( CODA_RENAME ) ;
inp - > coda_rename . sourceFid = * old_fid ;
inp - > coda_rename . destFid = * new_fid ;
inp - > coda_rename . srcname = offset ;
/* Venus must receive an null terminated string */
s = ( old_length & ~ 0x3 ) + 4 ; /* round up to word boundary */
memcpy ( ( char * ) ( inp ) + offset , old_name , old_length ) ;
* ( ( char * ) inp + offset + old_length ) = ' \0 ' ;
/* another null terminated string for Venus */
offset + = s ;
inp - > coda_rename . destname = offset ;
s = ( new_length & ~ 0x3 ) + 4 ; /* round up to word boundary */
memcpy ( ( char * ) ( inp ) + offset , new_name , new_length ) ;
* ( ( char * ) inp + offset + new_length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_create ( struct super_block * sb , struct CodaFid * dirfid ,
const char * name , int length , int excl , int mode ,
struct CodaFid * newfid , struct coda_vattr * attrs )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset ;
offset = INSIZE ( create ) ;
insize = max_t ( unsigned int , offset + length + 1 , OUTSIZE ( create ) ) ;
UPARG ( CODA_CREATE ) ;
inp - > coda_create . VFid = * dirfid ;
inp - > coda_create . attr . va_mode = mode ;
inp - > coda_create . excl = excl ;
inp - > coda_create . mode = mode ;
inp - > coda_create . name = offset ;
/* Venus must get null terminated string */
memcpy ( ( char * ) ( inp ) + offset , name , length ) ;
* ( ( char * ) inp + offset + length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
* attrs = outp - > coda_create . attr ;
* newfid = outp - > coda_create . VFid ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_rmdir ( struct super_block * sb , struct CodaFid * dirfid ,
const char * name , int length )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset ;
offset = INSIZE ( rmdir ) ;
insize = max_t ( unsigned int , offset + length + 1 , OUTSIZE ( rmdir ) ) ;
UPARG ( CODA_RMDIR ) ;
inp - > coda_rmdir . VFid = * dirfid ;
inp - > coda_rmdir . name = offset ;
memcpy ( ( char * ) ( inp ) + offset , name , length ) ;
* ( ( char * ) inp + offset + length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_remove ( struct super_block * sb , struct CodaFid * dirfid ,
const char * name , int length )
{
union inputArgs * inp ;
union outputArgs * outp ;
int error = 0 , insize , outsize , offset ;
offset = INSIZE ( remove ) ;
insize = max_t ( unsigned int , offset + length + 1 , OUTSIZE ( remove ) ) ;
UPARG ( CODA_REMOVE ) ;
inp - > coda_remove . VFid = * dirfid ;
inp - > coda_remove . name = offset ;
memcpy ( ( char * ) ( inp ) + offset , name , length ) ;
* ( ( char * ) inp + offset + length ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_readlink ( struct super_block * sb , struct CodaFid * fid ,
char * buffer , int * length )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int retlen ;
char * result ;
insize = max_t ( unsigned int ,
INSIZE ( readlink ) , OUTSIZE ( readlink ) + * length + 1 ) ;
UPARG ( CODA_READLINK ) ;
inp - > coda_readlink . VFid = * fid ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
if ( ! error ) {
retlen = outp - > coda_readlink . count ;
if ( retlen > * length )
retlen = * length ;
* length = retlen ;
result = ( char * ) outp + ( long ) outp - > coda_readlink . data ;
memcpy ( buffer , result , retlen ) ;
* ( buffer + retlen ) = ' \0 ' ;
}
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_link ( struct super_block * sb , struct CodaFid * fid ,
struct CodaFid * dirfid , const char * name , int len )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset ;
offset = INSIZE ( link ) ;
insize = max_t ( unsigned int , offset + len + 1 , OUTSIZE ( link ) ) ;
UPARG ( CODA_LINK ) ;
inp - > coda_link . sourceFid = * fid ;
inp - > coda_link . destFid = * dirfid ;
inp - > coda_link . tname = offset ;
/* make sure strings are null terminated */
memcpy ( ( char * ) ( inp ) + offset , name , len ) ;
* ( ( char * ) inp + offset + len ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_symlink ( struct super_block * sb , struct CodaFid * fid ,
const char * name , int len ,
const char * symname , int symlen )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int offset , s ;
offset = INSIZE ( symlink ) ;
insize = max_t ( unsigned int , offset + len + symlen + 8 , OUTSIZE ( symlink ) ) ;
UPARG ( CODA_SYMLINK ) ;
/* inp->coda_symlink.attr = *tva; XXXXXX */
inp - > coda_symlink . VFid = * fid ;
/* Round up to word boundary and null terminate */
inp - > coda_symlink . srcname = offset ;
s = ( symlen & ~ 0x3 ) + 4 ;
memcpy ( ( char * ) ( inp ) + offset , symname , symlen ) ;
* ( ( char * ) inp + offset + symlen ) = ' \0 ' ;
/* Round up to word boundary and null terminate */
offset + = s ;
inp - > coda_symlink . tname = offset ;
s = ( len & ~ 0x3 ) + 4 ;
memcpy ( ( char * ) ( inp ) + offset , name , len ) ;
* ( ( char * ) inp + offset + len ) = ' \0 ' ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_fsync ( struct super_block * sb , struct CodaFid * fid )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( fsync ) ;
UPARG ( CODA_FSYNC ) ;
inp - > coda_fsync . VFid = * fid ;
error = coda_upcall ( coda_sbp ( sb ) , sizeof ( union inputArgs ) ,
& outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_access ( struct super_block * sb , struct CodaFid * fid , int mask )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = SIZE ( access ) ;
UPARG ( CODA_ACCESS ) ;
inp - > coda_access . VFid = * fid ;
inp - > coda_access . flags = mask ;
error = coda_upcall ( coda_sbp ( sb ) , insize , & outsize , inp ) ;
CODA_FREE ( inp , insize ) ;
return error ;
}
int venus_pioctl ( struct super_block * sb , struct CodaFid * fid ,
unsigned int cmd , struct PioctlData * data )
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
int iocsize ;
insize = VC_MAXMSGSIZE ;
UPARG ( CODA_IOCTL ) ;
/* build packet for Venus */
if ( data - > vi . in_size > VC_MAXDATASIZE ) {
error = - EINVAL ;
goto exit ;
}
if ( data - > vi . out_size > VC_MAXDATASIZE ) {
error = - EINVAL ;
goto exit ;
}
inp - > coda_ioctl . VFid = * fid ;
/* the cmd field was mutated by increasing its size field to
* reflect the path and follow args . We need to subtract that
* out before sending the command to Venus . */
inp - > coda_ioctl . cmd = ( cmd & ~ ( PIOCPARM_MASK < < 16 ) ) ;
iocsize = ( ( cmd > > 16 ) & PIOCPARM_MASK ) - sizeof ( char * ) - sizeof ( int ) ;
inp - > coda_ioctl . cmd | = ( iocsize & PIOCPARM_MASK ) < < 16 ;
/* in->coda_ioctl.rwflag = flag; */
inp - > coda_ioctl . len = data - > vi . in_size ;
inp - > coda_ioctl . data = ( char * ) ( INSIZE ( ioctl ) ) ;
/* get the data out of user space */
if ( copy_from_user ( ( char * ) inp + ( long ) inp - > coda_ioctl . data ,
data - > vi . in , data - > vi . in_size ) ) {
error = - EINVAL ;
goto exit ;
}
error = coda_upcall ( coda_sbp ( sb ) , SIZE ( ioctl ) + data - > vi . in_size ,
& outsize , inp ) ;
if ( error ) {
printk ( " coda_pioctl: Venus returns: %d for %s \n " ,
error , coda_f2s ( fid ) ) ;
goto exit ;
}
if ( outsize < ( long ) outp - > coda_ioctl . data + outp - > coda_ioctl . len ) {
error = - EINVAL ;
goto exit ;
}
/* Copy out the OUT buffer. */
if ( outp - > coda_ioctl . len > data - > vi . out_size ) {
error = - EINVAL ;
goto exit ;
}
/* Copy out the OUT buffer. */
if ( copy_to_user ( data - > vi . out ,
( char * ) outp + ( long ) outp - > coda_ioctl . data ,
outp - > coda_ioctl . len ) ) {
error = - EFAULT ;
goto exit ;
}
exit :
CODA_FREE ( inp , insize ) ;
return error ;
}
2006-06-23 13:02:58 +04:00
int venus_statfs ( struct dentry * dentry , struct kstatfs * sfs )
2005-04-17 02:20:36 +04:00
{
union inputArgs * inp ;
union outputArgs * outp ;
int insize , outsize , error ;
insize = max_t ( unsigned int , INSIZE ( statfs ) , OUTSIZE ( statfs ) ) ;
UPARG ( CODA_STATFS ) ;
2006-06-23 13:02:58 +04:00
error = coda_upcall ( coda_sbp ( dentry - > d_sb ) , insize , & outsize , inp ) ;
2005-04-17 02:20:36 +04:00
if ( ! error ) {
sfs - > f_blocks = outp - > coda_statfs . stat . f_blocks ;
sfs - > f_bfree = outp - > coda_statfs . stat . f_bfree ;
sfs - > f_bavail = outp - > coda_statfs . stat . f_bavail ;
sfs - > f_files = outp - > coda_statfs . stat . f_files ;
sfs - > f_ffree = outp - > coda_statfs . stat . f_ffree ;
} else {
printk ( " coda_statfs: Venus returns: %d \n " , error ) ;
}
CODA_FREE ( inp , insize ) ;
return error ;
}
/*
* coda_upcall and coda_downcall routines .
*
*/
static inline void coda_waitfor_upcall ( struct upc_req * vmp ,
struct venus_comm * vcommp )
{
DECLARE_WAITQUEUE ( wait , current ) ;
vmp - > uc_posttime = jiffies ;
add_wait_queue ( & vmp - > uc_sleep , & wait ) ;
for ( ; ; ) {
if ( ! coda_hard & & vmp - > uc_opcode ! = CODA_CLOSE )
set_current_state ( TASK_INTERRUPTIBLE ) ;
else
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
/* venus died */
if ( ! vcommp - > vc_inuse )
break ;
/* got a reply */
if ( vmp - > uc_flags & ( REQ_WRITE | REQ_ABORT ) )
break ;
if ( ! coda_hard & & vmp - > uc_opcode ! = CODA_CLOSE & & signal_pending ( current ) ) {
/* if this process really wants to die, let it go */
if ( sigismember ( & ( current - > pending . signal ) , SIGKILL ) | |
sigismember ( & ( current - > pending . signal ) , SIGINT ) )
break ;
/* signal is present: after timeout always return
really smart idea , probably useless . . . */
if ( jiffies - vmp - > uc_posttime > coda_timeout * HZ )
break ;
}
schedule ( ) ;
}
remove_wait_queue ( & vmp - > uc_sleep , & wait ) ;
set_current_state ( TASK_RUNNING ) ;
return ;
}
/*
* coda_upcall will return an error in the case of
* failed communication with Venus _or_ will peek at Venus
* reply and return Venus ' error .
*
* As venus has 2 types of errors , normal errors ( positive ) and internal
* errors ( negative ) , normal errors are negated , while internal errors
* are all mapped to - EINTR , while showing a nice warning message . ( jh )
*
*/
static int coda_upcall ( struct coda_sb_info * sbi ,
int inSize , int * outSize ,
union inputArgs * buffer )
{
struct venus_comm * vcommp ;
union outputArgs * out ;
struct upc_req * req ;
int error = 0 ;
vcommp = sbi - > sbi_vcomm ;
if ( ! vcommp - > vc_inuse ) {
printk ( " No pseudo device in upcall comms at %p \n " , vcommp ) ;
return - ENXIO ;
}
/* Format the request message. */
req = upc_alloc ( ) ;
if ( ! req ) {
printk ( " Failed to allocate upc_req structure \n " ) ;
return - ENOMEM ;
}
req - > uc_data = ( void * ) buffer ;
req - > uc_flags = 0 ;
req - > uc_inSize = inSize ;
req - > uc_outSize = * outSize ? * outSize : inSize ;
req - > uc_opcode = ( ( union inputArgs * ) buffer ) - > ih . opcode ;
req - > uc_unique = + + vcommp - > vc_seq ;
init_waitqueue_head ( & req - > uc_sleep ) ;
/* Fill in the common input args. */
( ( union inputArgs * ) buffer ) - > ih . unique = req - > uc_unique ;
/* Append msg to pending queue and poke Venus. */
2006-06-26 11:24:37 +04:00
list_add_tail ( & ( req - > uc_chain ) , & vcommp - > vc_pending ) ;
2005-04-17 02:20:36 +04:00
wake_up_interruptible ( & vcommp - > vc_waitq ) ;
/* We can be interrupted while we wait for Venus to process
* our request . If the interrupt occurs before Venus has read
* the request , we dequeue and return . If it occurs after the
* read but before the reply , we dequeue , send a signal
* message , and return . If it occurs after the reply we ignore
* it . In no case do we want to restart the syscall . If it
* was interrupted by a venus shutdown ( psdev_close ) , return
* ENODEV . */
/* Go to sleep. Wake up on signals only after the timeout. */
coda_waitfor_upcall ( req , vcommp ) ;
if ( vcommp - > vc_inuse ) { /* i.e. Venus is still alive */
/* Op went through, interrupt or not... */
if ( req - > uc_flags & REQ_WRITE ) {
out = ( union outputArgs * ) req - > uc_data ;
/* here we map positive Venus errors to kernel errors */
error = - out - > oh . result ;
* outSize = req - > uc_outSize ;
goto exit ;
}
if ( ! ( req - > uc_flags & REQ_READ ) & & signal_pending ( current ) ) {
/* Interrupted before venus read it. */
list_del ( & ( req - > uc_chain ) ) ;
/* perhaps the best way to convince the app to
give up ? */
error = - EINTR ;
goto exit ;
}
if ( ( req - > uc_flags & REQ_READ ) & & signal_pending ( current ) ) {
/* interrupted after Venus did its read, send signal */
union inputArgs * sig_inputArgs ;
struct upc_req * sig_req ;
list_del ( & ( req - > uc_chain ) ) ;
error = - ENOMEM ;
sig_req = upc_alloc ( ) ;
if ( ! sig_req ) goto exit ;
CODA_ALLOC ( ( sig_req - > uc_data ) , char * , sizeof ( struct coda_in_hdr ) ) ;
if ( ! sig_req - > uc_data ) {
upc_free ( sig_req ) ;
goto exit ;
}
error = - EINTR ;
sig_inputArgs = ( union inputArgs * ) sig_req - > uc_data ;
sig_inputArgs - > ih . opcode = CODA_SIGNAL ;
sig_inputArgs - > ih . unique = req - > uc_unique ;
sig_req - > uc_flags = REQ_ASYNC ;
sig_req - > uc_opcode = sig_inputArgs - > ih . opcode ;
sig_req - > uc_unique = sig_inputArgs - > ih . unique ;
sig_req - > uc_inSize = sizeof ( struct coda_in_hdr ) ;
sig_req - > uc_outSize = sizeof ( struct coda_in_hdr ) ;
/* insert at head of queue! */
list_add ( & ( sig_req - > uc_chain ) , & vcommp - > vc_pending ) ;
wake_up_interruptible ( & vcommp - > vc_waitq ) ;
} else {
printk ( " Coda: Strange interruption.. \n " ) ;
error = - EINTR ;
}
} else { /* If venus died i.e. !VC_OPEN(vcommp) */
printk ( " coda_upcall: Venus dead on (op,un) (%d.%d) flags %d \n " ,
req - > uc_opcode , req - > uc_unique , req - > uc_flags ) ;
error = - ENODEV ;
}
exit :
upc_free ( req ) ;
return error ;
}
/*
The statements below are part of the Coda opportunistic
programming - - taken from the Mach / BSD kernel code for Coda .
You don ' t get correct semantics by stating what needs to be
done without guaranteeing the invariants needed for it to happen .
When will be have time to find out what exactly is going on ? ( pjb )
*/
/*
* There are 7 cases where cache invalidations occur . The semantics
* of each is listed here :
*
* CODA_FLUSH - - flush all entries from the name cache and the cnode cache .
* CODA_PURGEUSER - - flush all entries from the name cache for a specific user
* This call is a result of token expiration .
*
* The next arise as the result of callbacks on a file or directory .
* CODA_ZAPFILE - - flush the cached attributes for a file .
* CODA_ZAPDIR - - flush the attributes for the dir and
* force a new lookup for all the children
of this dir .
*
* The next is a result of Venus detecting an inconsistent file .
* CODA_PURGEFID - - flush the attribute for the file
* purge it and its children from the dcache
*
* The last allows Venus to replace local fids with global ones
* during reintegration .
*
* CODA_REPLACE - - replace one CodaFid with another throughout the name cache */
int coda_downcall ( int opcode , union outputArgs * out , struct super_block * sb )
{
/* Handle invalidation requests. */
if ( ! sb | | ! sb - > s_root | | ! sb - > s_root - > d_inode )
return 0 ;
switch ( opcode ) {
case CODA_FLUSH : {
coda_cache_clear_all ( sb ) ;
shrink_dcache_sb ( sb ) ;
coda_flag_inode ( sb - > s_root - > d_inode , C_FLUSH ) ;
return ( 0 ) ;
}
case CODA_PURGEUSER : {
coda_cache_clear_all ( sb ) ;
return ( 0 ) ;
}
case CODA_ZAPDIR : {
struct inode * inode ;
struct CodaFid * fid = & out - > coda_zapdir . CodaFid ;
inode = coda_fid_to_inode ( fid , sb ) ;
if ( inode ) {
coda_flag_inode_children ( inode , C_PURGE ) ;
coda_flag_inode ( inode , C_VATTR ) ;
iput ( inode ) ;
}
return ( 0 ) ;
}
case CODA_ZAPFILE : {
struct inode * inode ;
struct CodaFid * fid = & out - > coda_zapfile . CodaFid ;
inode = coda_fid_to_inode ( fid , sb ) ;
if ( inode ) {
coda_flag_inode ( inode , C_VATTR ) ;
iput ( inode ) ;
}
return 0 ;
}
case CODA_PURGEFID : {
struct inode * inode ;
struct CodaFid * fid = & out - > coda_purgefid . CodaFid ;
inode = coda_fid_to_inode ( fid , sb ) ;
if ( inode ) {
coda_flag_inode_children ( inode , C_PURGE ) ;
/* catch the dentries later if some are still busy */
coda_flag_inode ( inode , C_PURGE ) ;
d_prune_aliases ( inode ) ;
iput ( inode ) ;
}
return 0 ;
}
case CODA_REPLACE : {
struct inode * inode ;
struct CodaFid * oldfid = & out - > coda_replace . OldFid ;
struct CodaFid * newfid = & out - > coda_replace . NewFid ;
inode = coda_fid_to_inode ( oldfid , sb ) ;
if ( inode ) {
coda_replace_fid ( inode , oldfid , newfid ) ;
iput ( inode ) ;
}
return 0 ;
}
}
return 0 ;
}