2005-04-17 02:20:36 +04:00
/*
* linux / fs / lockd / svcsubs . c
*
* Various support routines for the NLM server .
*
* Copyright ( C ) 1996 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/types.h>
# include <linux/string.h>
# include <linux/time.h>
# include <linux/in.h>
2006-03-26 13:37:12 +04:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/sunrpc/svc.h>
# include <linux/sunrpc/clnt.h>
# include <linux/nfsd/nfsfh.h>
# include <linux/nfsd/export.h>
# include <linux/lockd/lockd.h>
# include <linux/lockd/share.h>
# include <linux/lockd/sm_inter.h>
# define NLMDBG_FACILITY NLMDBG_SVCSUBS
/*
* Global file hash table
*/
2006-10-04 13:15:58 +04:00
# define FILE_HASH_BITS 7
2005-04-17 02:20:36 +04:00
# define FILE_NRHASH (1<<FILE_HASH_BITS)
2006-10-04 13:15:58 +04:00
static struct hlist_head nlm_files [ FILE_NRHASH ] ;
2006-03-26 13:37:12 +04:00
static DEFINE_MUTEX ( nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-02 00:53:32 +03:00
# ifdef NFSD_DEBUG
static inline void nlm_debug_print_fh ( char * msg , struct nfs_fh * f )
{
u32 * fhp = ( u32 * ) f - > data ;
/* print the first 32 bytes of the fh */
dprintk ( " lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x) \n " ,
msg , fhp [ 0 ] , fhp [ 1 ] , fhp [ 2 ] , fhp [ 3 ] ,
fhp [ 4 ] , fhp [ 5 ] , fhp [ 6 ] , fhp [ 7 ] ) ;
}
static inline void nlm_debug_print_file ( char * msg , struct nlm_file * file )
{
2006-12-08 13:37:18 +03:00
struct inode * inode = file - > f_file - > f_path . dentry - > d_inode ;
2005-11-02 00:53:32 +03:00
dprintk ( " lockd: %s %s/%ld \n " ,
msg , inode - > i_sb - > s_id , inode - > i_ino ) ;
}
# else
static inline void nlm_debug_print_fh ( char * msg , struct nfs_fh * f )
{
return ;
}
static inline void nlm_debug_print_file ( char * msg , struct nlm_file * file )
{
return ;
}
# endif
2005-04-17 02:20:36 +04:00
static inline unsigned int file_hash ( struct nfs_fh * f )
{
unsigned int tmp = 0 ;
int i ;
for ( i = 0 ; i < NFS2_FHSIZE ; i + + )
tmp + = f - > data [ i ] ;
return tmp & ( FILE_NRHASH - 1 ) ;
}
/*
* Lookup file info . If it doesn ' t exist , create a file info struct
* and open a ( VFS ) file for the given inode .
*
* FIXME :
* Note that we open the file O_RDONLY even when creating write locks .
* This is not quite right , but for now , we assume the client performs
* the proper R / W checking .
*/
2006-10-20 10:28:46 +04:00
__be32
2005-04-17 02:20:36 +04:00
nlm_lookup_file ( struct svc_rqst * rqstp , struct nlm_file * * result ,
struct nfs_fh * f )
{
2006-10-04 13:15:58 +04:00
struct hlist_node * pos ;
2005-04-17 02:20:36 +04:00
struct nlm_file * file ;
unsigned int hash ;
2006-10-20 10:28:46 +04:00
__be32 nfserr ;
2005-04-17 02:20:36 +04:00
2005-11-02 00:53:32 +03:00
nlm_debug_print_fh ( " nlm_file_lookup " , f ) ;
2005-04-17 02:20:36 +04:00
hash = file_hash ( f ) ;
/* Lock file table */
2006-03-26 13:37:12 +04:00
mutex_lock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
2006-10-04 13:15:58 +04:00
hlist_for_each_entry ( file , pos , & nlm_files [ hash ] , f_list )
2005-04-17 02:20:36 +04:00
if ( ! nfs_compare_fh ( & file - > f_handle , f ) )
goto found ;
2005-11-02 00:53:32 +03:00
nlm_debug_print_fh ( " creating file for " , f ) ;
2005-04-17 02:20:36 +04:00
nfserr = nlm_lck_denied_nolocks ;
2006-09-27 12:49:37 +04:00
file = kzalloc ( sizeof ( * file ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! file )
goto out_unlock ;
memcpy ( & file - > f_handle , f , sizeof ( struct nfs_fh ) ) ;
2006-10-04 13:16:06 +04:00
mutex_init ( & file - > f_mutex ) ;
2006-10-04 13:15:58 +04:00
INIT_HLIST_NODE ( & file - > f_list ) ;
2006-10-04 13:15:57 +04:00
INIT_LIST_HEAD ( & file - > f_blocks ) ;
2005-04-17 02:20:36 +04:00
/* Open the file. Note that this must not sleep for too long, else
* we would lock up lockd : - ) So no NFS re - exports , folks .
*
* We have to make sure we have the right credential to open
* the file .
*/
if ( ( nfserr = nlmsvc_ops - > fopen ( rqstp , f , & file - > f_file ) ) ! = 0 ) {
2006-10-04 13:15:54 +04:00
dprintk ( " lockd: open failed (error %d) \n " , nfserr ) ;
2005-04-17 02:20:36 +04:00
goto out_free ;
}
2006-10-04 13:15:58 +04:00
hlist_add_head ( & file - > f_list , & nlm_files [ hash ] ) ;
2005-04-17 02:20:36 +04:00
found :
dprintk ( " lockd: found file %p (count %d) \n " , file , file - > f_count ) ;
* result = file ;
file - > f_count + + ;
nfserr = 0 ;
out_unlock :
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
return nfserr ;
out_free :
kfree ( file ) ;
goto out_unlock ;
}
/*
* Delete a file after having released all locks , blocks and shares
*/
static inline void
nlm_delete_file ( struct nlm_file * file )
{
2005-11-02 00:53:32 +03:00
nlm_debug_print_file ( " closing file " , file ) ;
2006-10-04 13:15:58 +04:00
if ( ! hlist_unhashed ( & file - > f_list ) ) {
hlist_del ( & file - > f_list ) ;
nlmsvc_ops - > fclose ( file - > f_file ) ;
kfree ( file ) ;
} else {
printk ( KERN_WARNING " lockd: attempt to release unknown file! \n " ) ;
2005-04-17 02:20:36 +04:00
}
}
/*
* Loop over all locks on the given file and perform the specified
* action .
*/
static int
2006-10-04 13:15:59 +04:00
nlm_traverse_locks ( struct nlm_host * host , struct nlm_file * file ,
nlm_host_match_fn_t match )
2005-04-17 02:20:36 +04:00
{
struct inode * inode = nlmsvc_file_inode ( file ) ;
struct file_lock * fl ;
struct nlm_host * lockhost ;
again :
file - > f_locks = 0 ;
for ( fl = inode - > i_flock ; fl ; fl = fl - > fl_next ) {
2006-03-20 21:44:26 +03:00
if ( fl - > fl_lmops ! = & nlmsvc_lock_operations )
2005-04-17 02:20:36 +04:00
continue ;
/* update current lock count */
file - > f_locks + + ;
2006-10-04 13:15:59 +04:00
2005-04-17 02:20:36 +04:00
lockhost = ( struct nlm_host * ) fl - > fl_owner ;
2006-10-04 13:15:59 +04:00
if ( match ( lockhost , host ) ) {
2005-04-17 02:20:36 +04:00
struct file_lock lock = * fl ;
lock . fl_type = F_UNLCK ;
lock . fl_start = 0 ;
lock . fl_end = OFFSET_MAX ;
2006-11-29 00:27:06 +03:00
if ( vfs_lock_file ( file - > f_file , F_SETLK , & lock , NULL ) < 0 ) {
2005-04-17 02:20:36 +04:00
printk ( " lockd: unlock failure in %s:%d \n " ,
__FILE__ , __LINE__ ) ;
return 1 ;
}
goto again ;
}
}
return 0 ;
}
/*
2006-10-04 13:15:59 +04:00
* Inspect a single file
*/
static inline int
nlm_inspect_file ( struct nlm_host * host , struct nlm_file * file , nlm_host_match_fn_t match )
{
nlmsvc_traverse_blocks ( host , file , match ) ;
nlmsvc_traverse_shares ( host , file , match ) ;
return nlm_traverse_locks ( host , file , match ) ;
}
/*
* Quick check whether there are still any locks , blocks or
* shares on a given file .
2005-04-17 02:20:36 +04:00
*/
static inline int
2006-10-04 13:15:59 +04:00
nlm_file_inuse ( struct nlm_file * file )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:15:59 +04:00
struct inode * inode = nlmsvc_file_inode ( file ) ;
struct file_lock * fl ;
if ( file - > f_count | | ! list_empty ( & file - > f_blocks ) | | file - > f_shares )
return 1 ;
for ( fl = inode - > i_flock ; fl ; fl = fl - > fl_next ) {
if ( fl - > fl_lmops = = & nlmsvc_lock_operations )
2005-04-17 02:20:36 +04:00
return 1 ;
}
2006-10-04 13:15:59 +04:00
file - > f_locks = 0 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Loop over all files in the file table .
*/
static int
2006-10-04 13:15:59 +04:00
nlm_traverse_files ( struct nlm_host * host , nlm_host_match_fn_t match )
2005-04-17 02:20:36 +04:00
{
2006-10-04 13:15:58 +04:00
struct hlist_node * pos , * next ;
struct nlm_file * file ;
2006-08-10 19:58:57 +04:00
int i , ret = 0 ;
2005-04-17 02:20:36 +04:00
2006-03-26 13:37:12 +04:00
mutex_lock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < FILE_NRHASH ; i + + ) {
2006-10-04 13:15:58 +04:00
hlist_for_each_entry_safe ( file , pos , next , & nlm_files [ i ] , f_list ) {
2006-08-10 19:58:57 +04:00
file - > f_count + + ;
mutex_unlock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
/* Traverse locks, blocks and shares of this file
* and update file - > f_locks count */
2006-10-04 13:15:59 +04:00
if ( nlm_inspect_file ( host , file , match ) )
2006-08-10 19:58:57 +04:00
ret = 1 ;
2005-04-17 02:20:36 +04:00
2006-08-10 19:58:57 +04:00
mutex_lock ( & nlm_file_mutex ) ;
file - > f_count - - ;
2005-04-17 02:20:36 +04:00
/* No more references to this file. Let go of it. */
2006-10-04 13:15:57 +04:00
if ( list_empty ( & file - > f_blocks ) & & ! file - > f_locks
2005-04-17 02:20:36 +04:00
& & ! file - > f_shares & & ! file - > f_count ) {
2006-10-04 13:15:58 +04:00
hlist_del ( & file - > f_list ) ;
2005-04-17 02:20:36 +04:00
nlmsvc_ops - > fclose ( file - > f_file ) ;
kfree ( file ) ;
}
}
}
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nlm_file_mutex ) ;
2006-08-10 19:58:57 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Release file . If there are no more remote locks on this file ,
* close it and free the handle .
*
* Note that we can ' t do proper reference counting without major
* contortions because the code in fs / locks . c creates , deletes and
* splits locks without notification . Our only way is to walk the
* entire lock list each time we remove a lock .
*/
void
nlm_release_file ( struct nlm_file * file )
{
dprintk ( " lockd: nlm_release_file(%p, ct = %d) \n " ,
file , file - > f_count ) ;
/* Lock file table */
2006-03-26 13:37:12 +04:00
mutex_lock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
/* If there are no more locks etc, delete the file */
2006-10-04 13:15:59 +04:00
if ( - - file - > f_count = = 0 & & ! nlm_file_inuse ( file ) )
nlm_delete_file ( file ) ;
2005-04-17 02:20:36 +04:00
2006-03-26 13:37:12 +04:00
mutex_unlock ( & nlm_file_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-04 13:15:59 +04:00
/*
* Helpers function for resource traversal
*
* nlmsvc_mark_host :
* used by the garbage collector ; simply sets h_inuse .
* Always returns 0.
*
* nlmsvc_same_host :
* returns 1 iff the two hosts match . Used to release
* all resources bound to a specific host .
*
* nlmsvc_is_client :
* returns 1 iff the host is a client .
* Used by nlmsvc_invalidate_all
*/
static int
nlmsvc_mark_host ( struct nlm_host * host , struct nlm_host * dummy )
{
host - > h_inuse = 1 ;
return 0 ;
}
static int
nlmsvc_same_host ( struct nlm_host * host , struct nlm_host * other )
{
return host = = other ;
}
static int
nlmsvc_is_client ( struct nlm_host * host , struct nlm_host * dummy )
{
2006-10-17 11:10:17 +04:00
if ( host - > h_server ) {
/* we are destroying locks even though the client
* hasn ' t asked us too , so don ' t unmonitor the
* client
*/
if ( host - > h_nsmhandle )
host - > h_nsmhandle - > sm_sticky = 1 ;
return 1 ;
} else
return 0 ;
2006-10-04 13:15:59 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Mark all hosts that still hold resources
*/
void
nlmsvc_mark_resources ( void )
{
dprintk ( " lockd: nlmsvc_mark_resources \n " ) ;
2006-10-04 13:15:59 +04:00
nlm_traverse_files ( NULL , nlmsvc_mark_host ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Release all resources held by the given client
*/
void
nlmsvc_free_host_resources ( struct nlm_host * host )
{
dprintk ( " lockd: nlmsvc_free_host_resources \n " ) ;
2006-10-04 13:15:59 +04:00
if ( nlm_traverse_files ( host , nlmsvc_same_host ) ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_WARNING
2006-10-04 13:15:54 +04:00
" lockd: couldn't remove all locks held by %s \n " ,
2005-04-17 02:20:36 +04:00
host - > h_name ) ;
2006-10-04 13:15:54 +04:00
BUG ( ) ;
}
2005-04-17 02:20:36 +04:00
}
/*
2006-10-04 13:16:00 +04:00
* Remove all locks held for clients
2005-04-17 02:20:36 +04:00
*/
void
nlmsvc_invalidate_all ( void )
{
2006-10-04 13:15:59 +04:00
/* Release all locks held by NFS clients.
* Previously , the code would call
* nlmsvc_free_host_resources for each client in
* turn , which is about as inefficient as it gets .
* Now we just do it once in nlm_traverse_files .
*/
nlm_traverse_files ( NULL , nlmsvc_is_client ) ;
2005-04-17 02:20:36 +04:00
}