2005-04-16 15:20:36 -07:00
/*
* linux / fs / lockd / host . c
*
* Management for NLM peer hosts . The nlm_host struct is shared
* between client and server implementation . The only reason to
* do so is to reduce code bloat .
*
* Copyright ( C ) 1996 , Olaf Kirch < okir @ monad . swb . de >
*/
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/in.h>
2008-08-27 16:57:23 -04:00
# include <linux/in6.h>
2005-04-16 15:20:36 -07:00
# include <linux/sunrpc/clnt.h>
# include <linux/sunrpc/svc.h>
# include <linux/lockd/lockd.h>
# include <linux/lockd/sm_inter.h>
2006-03-26 01:37:12 -08:00
# include <linux/mutex.h>
2005-04-16 15:20:36 -07:00
2008-08-27 16:57:23 -04:00
# include <net/ipv6.h>
2005-04-16 15:20:36 -07:00
# define NLMDBG_FACILITY NLMDBG_HOSTCACHE
# define NLM_HOST_NRHASH 32
# define NLM_HOST_REBIND (60 * HZ)
2008-02-08 13:03:37 +11:00
# define NLM_HOST_EXPIRE (300 * HZ)
# define NLM_HOST_COLLECT (120 * HZ)
2005-04-16 15:20:36 -07:00
2006-10-04 02:15:56 -07:00
static struct hlist_head nlm_hosts [ NLM_HOST_NRHASH ] ;
2005-04-16 15:20:36 -07:00
static unsigned long next_gc ;
static int nrhosts ;
2006-03-26 01:37:12 -08:00
static DEFINE_MUTEX ( nlm_host_mutex ) ;
2005-04-16 15:20:36 -07:00
static void nlm_gc_hosts ( void ) ;
2008-09-03 14:36:23 -04:00
static struct nsm_handle * nsm_find ( const struct sockaddr * sap ,
const size_t salen ,
2008-09-03 14:36:16 -04:00
const char * hostname ,
const size_t hostname_len ,
const int create ) ;
2005-04-16 15:20:36 -07:00
2008-10-03 12:50:07 -04:00
struct nlm_lookup_host_info {
const int server ; /* search for server|client */
2008-10-03 12:50:14 -04:00
const struct sockaddr * sap ; /* address to search for */
const size_t salen ; /* it's length */
2008-10-03 12:50:07 -04:00
const unsigned short protocol ; /* transport to search for*/
const u32 version ; /* NLM version to search for */
const char * hostname ; /* remote's hostname */
const size_t hostname_len ; /* it's length */
2008-10-03 12:50:14 -04:00
const struct sockaddr * src_sap ; /* our address (optional) */
2008-10-03 12:50:07 -04:00
const size_t src_len ; /* it's length */
} ;
2008-09-03 14:36:08 -04:00
/*
* Hash function must work well on big - and little - endian platforms
*/
static unsigned int __nlm_hash32 ( const __be32 n )
{
unsigned int hash = ( __force u32 ) n ^ ( ( __force u32 ) n > > 16 ) ;
return hash ^ ( hash > > 8 ) ;
}
static unsigned int __nlm_hash_addr4 ( const struct sockaddr * sap )
{
const struct sockaddr_in * sin = ( struct sockaddr_in * ) sap ;
return __nlm_hash32 ( sin - > sin_addr . s_addr ) ;
}
static unsigned int __nlm_hash_addr6 ( const struct sockaddr * sap )
{
const struct sockaddr_in6 * sin6 = ( struct sockaddr_in6 * ) sap ;
const struct in6_addr addr = sin6 - > sin6_addr ;
return __nlm_hash32 ( addr . s6_addr32 [ 0 ] ) ^
__nlm_hash32 ( addr . s6_addr32 [ 1 ] ) ^
__nlm_hash32 ( addr . s6_addr32 [ 2 ] ) ^
__nlm_hash32 ( addr . s6_addr32 [ 3 ] ) ;
}
static unsigned int nlm_hash_address ( const struct sockaddr * sap )
{
unsigned int hash ;
switch ( sap - > sa_family ) {
case AF_INET :
hash = __nlm_hash_addr4 ( sap ) ;
break ;
case AF_INET6 :
hash = __nlm_hash_addr6 ( sap ) ;
break ;
default :
hash = 0 ;
}
return hash & ( NLM_HOST_NRHASH - 1 ) ;
}
2008-08-27 16:57:38 -04:00
static void nlm_clear_port ( struct sockaddr * sap )
{
switch ( sap - > sa_family ) {
case AF_INET :
( ( struct sockaddr_in * ) sap ) - > sin_port = 0 ;
break ;
case AF_INET6 :
( ( struct sockaddr_in6 * ) sap ) - > sin6_port = 0 ;
break ;
}
}
2008-08-27 16:57:23 -04:00
static void nlm_display_address ( const struct sockaddr * sap ,
char * buf , const size_t len )
{
const struct sockaddr_in * sin = ( struct sockaddr_in * ) sap ;
const struct sockaddr_in6 * sin6 = ( struct sockaddr_in6 * ) sap ;
switch ( sap - > sa_family ) {
case AF_UNSPEC :
snprintf ( buf , len , " unspecified " ) ;
break ;
case AF_INET :
2008-10-31 00:56:28 -07:00
snprintf ( buf , len , " %pI4 " , & sin - > sin_addr . s_addr ) ;
2008-08-27 16:57:23 -04:00
break ;
case AF_INET6 :
if ( ipv6_addr_v4mapped ( & sin6 - > sin6_addr ) )
2008-10-31 00:56:28 -07:00
snprintf ( buf , len , " %pI4 " ,
& sin6 - > sin6_addr . s6_addr32 [ 3 ] ) ;
2008-08-27 16:57:23 -04:00
else
2008-10-29 12:52:50 -07:00
snprintf ( buf , len , " %pI6 " , & sin6 - > sin6_addr ) ;
2008-08-27 16:57:23 -04:00
break ;
default :
snprintf ( buf , len , " unsupported address family " ) ;
break ;
}
}
2005-04-16 15:20:36 -07:00
/*
* Common host lookup routine for server & client
*/
2008-10-03 12:50:07 -04:00
static struct nlm_host * nlm_lookup_host ( struct nlm_lookup_host_info * ni )
2005-04-16 15:20:36 -07:00
{
2006-10-04 02:15:56 -07:00
struct hlist_head * chain ;
struct hlist_node * pos ;
struct nlm_host * host ;
2006-10-04 02:15:53 -07:00
struct nsm_handle * nsm = NULL ;
2005-04-16 15:20:36 -07:00
2006-03-26 01:37:12 -08:00
mutex_lock ( & nlm_host_mutex ) ;
2005-04-16 15:20:36 -07:00
if ( time_after_eq ( jiffies , next_gc ) )
nlm_gc_hosts ( ) ;
2006-10-04 02:15:53 -07:00
/* We may keep several nlm_host objects for a peer, because each
* nlm_host is identified by
* ( address , protocol , version , server / client )
* We could probably simplify this a little by putting all those
* different NLM rpc_clients into one single nlm_host object .
* This would allow us to have one nlm_host per address .
*/
2008-10-03 12:50:14 -04:00
chain = & nlm_hosts [ nlm_hash_address ( ni - > sap ) ] ;
2006-10-04 02:15:56 -07:00
hlist_for_each_entry ( host , pos , chain , h_hash ) {
2008-10-03 12:50:14 -04:00
if ( ! nlm_cmp_addr ( nlm_addr ( host ) , ni - > sap ) )
2006-10-04 02:15:53 -07:00
continue ;
/* See if we have an NSM handle for this client */
2006-10-04 02:16:15 -07:00
if ( ! nsm )
nsm = host - > h_nsmhandle ;
2006-10-04 02:15:53 -07:00
2008-10-03 12:50:07 -04:00
if ( host - > h_proto ! = ni - > protocol )
2005-04-16 15:20:36 -07:00
continue ;
2008-10-03 12:50:07 -04:00
if ( host - > h_version ! = ni - > version )
2005-04-16 15:20:36 -07:00
continue ;
2008-10-03 12:50:07 -04:00
if ( host - > h_server ! = ni - > server )
2005-04-16 15:20:36 -07:00
continue ;
2008-10-03 12:50:14 -04:00
if ( ! nlm_cmp_addr ( nlm_srcaddr ( host ) , ni - > src_sap ) )
2007-07-09 22:25:29 +02:00
continue ;
2005-04-16 15:20:36 -07:00
2006-10-04 02:15:56 -07:00
/* Move to head of hash chain. */
hlist_del ( & host - > h_hash ) ;
hlist_add_head ( & host - > h_hash , chain ) ;
2006-10-04 02:15:54 -07:00
nlm_get_host ( host ) ;
2008-08-27 16:57:23 -04:00
dprintk ( " lockd: nlm_lookup_host found host %s (%s) \n " ,
host - > h_name , host - > h_addrbuf ) ;
2006-10-04 02:15:54 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
}
2008-08-27 16:57:15 -04:00
/*
* The host wasn ' t in our hash table . If we don ' t
* have an NSM handle for it yet , create one .
2006-10-04 02:15:53 -07:00
*/
2008-08-27 16:57:15 -04:00
if ( nsm )
atomic_inc ( & nsm - > sm_count ) ;
else {
host = NULL ;
2008-10-03 12:50:14 -04:00
nsm = nsm_find ( ni - > sap , ni - > salen ,
2008-10-03 12:50:07 -04:00
ni - > hostname , ni - > hostname_len , 1 ) ;
2008-08-27 16:57:23 -04:00
if ( ! nsm ) {
dprintk ( " lockd: nlm_lookup_host failed; "
" no nsm handle \n " ) ;
2008-08-27 16:57:15 -04:00
goto out ;
2008-08-27 16:57:23 -04:00
}
2008-08-27 16:57:15 -04:00
}
2005-04-16 15:20:36 -07:00
2006-09-27 01:49:37 -07:00
host = kzalloc ( sizeof ( * host ) , GFP_KERNEL ) ;
2006-10-04 02:15:53 -07:00
if ( ! host ) {
nsm_release ( nsm ) ;
2008-08-27 16:57:23 -04:00
dprintk ( " lockd: nlm_lookup_host failed; no memory \n " ) ;
2006-10-04 02:15:53 -07:00
goto out ;
}
host - > h_name = nsm - > sm_name ;
2008-10-03 12:50:14 -04:00
memcpy ( nlm_addr ( host ) , ni - > sap , ni - > salen ) ;
host - > h_addrlen = ni - > salen ;
2008-09-03 14:35:39 -04:00
nlm_clear_port ( nlm_addr ( host ) ) ;
2008-10-03 12:50:14 -04:00
memcpy ( nlm_srcaddr ( host ) , ni - > src_sap , ni - > src_len ) ;
2008-10-03 12:50:07 -04:00
host - > h_version = ni - > version ;
host - > h_proto = ni - > protocol ;
2005-04-16 15:20:36 -07:00
host - > h_rpcclnt = NULL ;
2006-06-09 09:40:24 -04:00
mutex_init ( & host - > h_mutex ) ;
2005-04-16 15:20:36 -07:00
host - > h_nextrebind = jiffies + NLM_HOST_REBIND ;
host - > h_expires = jiffies + NLM_HOST_EXPIRE ;
atomic_set ( & host - > h_count , 1 ) ;
init_waitqueue_head ( & host - > h_gracewait ) ;
2006-06-09 09:40:27 -04:00
init_rwsem ( & host - > h_rwsem ) ;
2005-04-16 15:20:36 -07:00
host - > h_state = 0 ; /* pseudo NSM state */
host - > h_nsmstate = 0 ; /* real NSM state */
2006-10-04 02:15:53 -07:00
host - > h_nsmhandle = nsm ;
2008-10-03 12:50:07 -04:00
host - > h_server = ni - > server ;
2006-10-04 02:15:56 -07:00
hlist_add_head ( & host - > h_hash , chain ) ;
2005-04-16 15:20:36 -07:00
INIT_LIST_HEAD ( & host - > h_lockowners ) ;
spin_lock_init ( & host - > h_lock ) ;
2006-03-20 13:44:40 -05:00
INIT_LIST_HEAD ( & host - > h_granted ) ;
INIT_LIST_HEAD ( & host - > h_reclaim ) ;
2005-04-16 15:20:36 -07:00
2008-02-08 13:03:37 +11:00
nrhosts + + ;
2008-08-27 16:57:23 -04:00
nlm_display_address ( ( struct sockaddr * ) & host - > h_addr ,
host - > h_addrbuf , sizeof ( host - > h_addrbuf ) ) ;
2008-09-03 14:35:46 -04:00
nlm_display_address ( ( struct sockaddr * ) & host - > h_srcaddr ,
host - > h_srcaddrbuf , sizeof ( host - > h_srcaddrbuf ) ) ;
2008-08-27 16:57:23 -04:00
dprintk ( " lockd: nlm_lookup_host created host %s \n " ,
host - > h_name ) ;
2006-10-04 02:15:53 -07:00
out :
2006-03-26 01:37:12 -08:00
mutex_unlock ( & nlm_host_mutex ) ;
2005-04-16 15:20:36 -07:00
return host ;
}
2006-10-04 02:16:00 -07:00
/*
* Destroy a host
*/
static void
nlm_destroy_host ( struct nlm_host * host )
{
struct rpc_clnt * clnt ;
BUG_ON ( ! list_empty ( & host - > h_lockowners ) ) ;
BUG_ON ( atomic_read ( & host - > h_count ) ) ;
/*
* Release NSM handle and unmonitor host .
*/
nsm_unmonitor ( host ) ;
2007-06-14 16:40:31 -04:00
clnt = host - > h_rpcclnt ;
if ( clnt ! = NULL )
rpc_shutdown_client ( clnt ) ;
2006-10-04 02:16:00 -07:00
kfree ( host ) ;
}
2008-10-03 12:50:21 -04:00
/**
* nlmclnt_lookup_host - Find an NLM host handle matching a remote server
* @ sap : network address of server
* @ salen : length of server address
* @ protocol : transport protocol to use
* @ version : NLM protocol version
* @ hostname : ' \0 ' - terminated hostname of server
*
* Returns an nlm_host structure that matches the passed - in
* [ server address , transport protocol , NLM version , server hostname ] .
* If one doesn ' t already exist in the host cache , a new handle is
* created and returned .
2006-12-06 20:38:29 -08:00
*/
2008-10-03 12:50:21 -04:00
struct nlm_host * nlmclnt_lookup_host ( const struct sockaddr * sap ,
const size_t salen ,
const unsigned short protocol ,
const u32 version , const char * hostname )
2006-12-06 20:38:29 -08:00
{
2008-10-03 12:50:14 -04:00
const struct sockaddr source = {
. sa_family = AF_UNSPEC ,
2008-08-27 16:57:31 -04:00
} ;
2008-10-03 12:50:07 -04:00
struct nlm_lookup_host_info ni = {
. server = 0 ,
2008-10-03 12:50:21 -04:00
. sap = sap ,
. salen = salen ,
. protocol = protocol ,
2008-10-03 12:50:07 -04:00
. version = version ,
. hostname = hostname ,
2008-10-03 12:50:21 -04:00
. hostname_len = strlen ( hostname ) ,
2008-10-03 12:50:14 -04:00
. src_sap = & source ,
. src_len = sizeof ( source ) ,
2008-10-03 12:50:07 -04:00
} ;
2007-07-09 22:25:29 +02:00
2008-10-03 12:50:07 -04:00
dprintk ( " lockd: %s(host='%s', vers=%u, proto=%s) \n " , __func__ ,
( hostname ? hostname : " <none> " ) , version ,
2008-10-03 12:50:21 -04:00
( protocol = = IPPROTO_UDP ? " udp " : " tcp " ) ) ;
2008-10-03 12:50:07 -04:00
return nlm_lookup_host ( & ni ) ;
2006-12-06 20:38:29 -08:00
}
2008-10-03 12:50:29 -04:00
/**
* nlmsvc_lookup_host - Find an NLM host handle matching a remote client
* @ rqstp : incoming NLM request
* @ hostname : name of client host
* @ hostname_len : length of client hostname
*
* Returns an nlm_host structure that matches the [ client address ,
* transport protocol , NLM version , client hostname ] of the passed - in
* NLM request . If one doesn ' t already exist in the host cache , a
* new handle is created and returned .
*
* Before possibly creating a new nlm_host , construct a sockaddr
* for a specific source address in case the local system has
* multiple network addresses . The family of the address in
* rq_daddr is guaranteed to be the same as the family of the
* address in rq_addr , so it ' s safe to use the same family for
* the source address .
2006-12-06 20:38:29 -08:00
*/
2008-10-03 12:50:29 -04:00
struct nlm_host * nlmsvc_lookup_host ( const struct svc_rqst * rqstp ,
const char * hostname ,
const size_t hostname_len )
2006-12-06 20:38:29 -08:00
{
2008-10-03 12:50:29 -04:00
struct sockaddr_in sin = {
2008-08-27 16:57:31 -04:00
. sin_family = AF_INET ,
2008-10-03 12:50:29 -04:00
} ;
struct sockaddr_in6 sin6 = {
. sin6_family = AF_INET6 ,
2008-08-27 16:57:31 -04:00
} ;
2008-10-03 12:50:07 -04:00
struct nlm_lookup_host_info ni = {
. server = 1 ,
2008-10-03 12:50:14 -04:00
. sap = svc_addr ( rqstp ) ,
. salen = rqstp - > rq_addrlen ,
2008-10-03 12:50:07 -04:00
. protocol = rqstp - > rq_prot ,
. version = rqstp - > rq_vers ,
. hostname = hostname ,
. hostname_len = hostname_len ,
2008-10-03 12:50:29 -04:00
. src_len = rqstp - > rq_addrlen ,
2008-10-03 12:50:07 -04:00
} ;
dprintk ( " lockd: %s(host='%*s', vers=%u, proto=%s) \n " , __func__ ,
( int ) hostname_len , hostname , rqstp - > rq_vers ,
( rqstp - > rq_prot = = IPPROTO_UDP ? " udp " : " tcp " ) ) ;
2007-07-09 22:25:29 +02:00
2008-10-03 12:50:29 -04:00
switch ( ni . sap - > sa_family ) {
case AF_INET :
sin . sin_addr . s_addr = rqstp - > rq_daddr . addr . s_addr ;
ni . src_sap = ( struct sockaddr * ) & sin ;
break ;
case AF_INET6 :
ipv6_addr_copy ( & sin6 . sin6_addr , & rqstp - > rq_daddr . addr6 ) ;
ni . src_sap = ( struct sockaddr * ) & sin6 ;
break ;
default :
return NULL ;
}
2008-10-03 12:50:07 -04:00
return nlm_lookup_host ( & ni ) ;
2006-12-06 20:38:29 -08:00
}
2005-04-16 15:20:36 -07:00
/*
* Create the NLM RPC client for an NLM peer
*/
struct rpc_clnt *
nlm_bind_host ( struct nlm_host * host )
{
struct rpc_clnt * clnt ;
2008-08-27 16:57:23 -04:00
dprintk ( " lockd: nlm_bind_host %s (%s), my addr=%s \n " ,
2008-09-03 14:35:46 -04:00
host - > h_name , host - > h_addrbuf , host - > h_srcaddrbuf ) ;
2005-04-16 15:20:36 -07:00
/* Lock host handle */
2006-06-09 09:40:24 -04:00
mutex_lock ( & host - > h_mutex ) ;
2005-04-16 15:20:36 -07:00
/* If we've already created an RPC client, check whether
* RPC rebind is required
*/
if ( ( clnt = host - > h_rpcclnt ) ! = NULL ) {
2005-08-25 16:25:49 -07:00
if ( time_after_eq ( jiffies , host - > h_nextrebind ) ) {
2006-01-03 09:55:50 +01:00
rpc_force_rebind ( clnt ) ;
2005-04-16 15:20:36 -07:00
host - > h_nextrebind = jiffies + NLM_HOST_REBIND ;
2008-08-27 16:57:23 -04:00
dprintk ( " lockd: next rebind in %lu jiffies \n " ,
2005-04-16 15:20:36 -07:00
host - > h_nextrebind - jiffies ) ;
}
} else {
2007-05-14 16:50:44 -04:00
unsigned long increment = nlmsvc_timeout ;
2006-08-22 20:06:20 -04:00
struct rpc_timeout timeparms = {
. to_initval = increment ,
. to_increment = increment ,
. to_maxval = increment * 6UL ,
. to_retries = 5U ,
} ;
struct rpc_create_args args = {
. protocol = host - > h_proto ,
2008-09-03 14:35:39 -04:00
. address = nlm_addr ( host ) ,
. addrsize = host - > h_addrlen ,
2008-09-03 14:35:46 -04:00
. saddress = nlm_srcaddr ( host ) ,
2006-08-22 20:06:20 -04:00
. timeout = & timeparms ,
. servername = host - > h_name ,
. program = & nlm_program ,
. version = host - > h_version ,
. authflavor = RPC_AUTH_UNIX ,
2008-02-06 11:34:11 -05:00
. flags = ( RPC_CLNT_CREATE_NOPING |
2006-08-22 20:06:20 -04:00
RPC_CLNT_CREATE_AUTOBIND ) ,
} ;
2008-02-06 11:34:11 -05:00
/*
* lockd retries server side blocks automatically so we want
* those to be soft RPC calls . Client side calls need to be
* hard RPC tasks .
*/
if ( ! host - > h_server )
args . flags | = RPC_CLNT_CREATE_HARDRTRY ;
2006-08-22 20:06:20 -04:00
clnt = rpc_create ( & args ) ;
if ( ! IS_ERR ( clnt ) )
host - > h_rpcclnt = clnt ;
else {
printk ( " lockd: couldn't create RPC handle for %s \n " , host - > h_name ) ;
clnt = NULL ;
}
2005-04-16 15:20:36 -07:00
}
2006-06-09 09:40:24 -04:00
mutex_unlock ( & host - > h_mutex ) ;
2005-04-16 15:20:36 -07:00
return clnt ;
}
/*
* Force a portmap lookup of the remote lockd port
*/
void
nlm_rebind_host ( struct nlm_host * host )
{
dprintk ( " lockd: rebind host %s \n " , host - > h_name ) ;
if ( host - > h_rpcclnt & & time_after_eq ( jiffies , host - > h_nextrebind ) ) {
2006-01-03 09:55:50 +01:00
rpc_force_rebind ( host - > h_rpcclnt ) ;
2005-04-16 15:20:36 -07:00
host - > h_nextrebind = jiffies + NLM_HOST_REBIND ;
}
}
/*
* Increment NLM host count
*/
struct nlm_host * nlm_get_host ( struct nlm_host * host )
{
if ( host ) {
dprintk ( " lockd: get host %s \n " , host - > h_name ) ;
atomic_inc ( & host - > h_count ) ;
host - > h_expires = jiffies + NLM_HOST_EXPIRE ;
}
return host ;
}
/*
* Release NLM host after use
*/
void nlm_release_host ( struct nlm_host * host )
{
if ( host ! = NULL ) {
dprintk ( " lockd: release host %s \n " , host - > h_name ) ;
BUG_ON ( atomic_read ( & host - > h_count ) < 0 ) ;
2006-03-20 13:44:41 -05:00
if ( atomic_dec_and_test ( & host - > h_count ) ) {
BUG_ON ( ! list_empty ( & host - > h_lockowners ) ) ;
BUG_ON ( ! list_empty ( & host - > h_granted ) ) ;
BUG_ON ( ! list_empty ( & host - > h_reclaim ) ) ;
}
2005-04-16 15:20:36 -07:00
}
}
2006-10-04 02:15:52 -07:00
/*
* We were notified that the host indicated by address & sin
* has rebooted .
* Release all resources held by that peer .
*/
2006-10-04 02:15:55 -07:00
void nlm_host_rebooted ( const struct sockaddr_in * sin ,
2007-11-01 16:56:53 -04:00
const char * hostname ,
unsigned int hostname_len ,
2006-10-04 02:15:55 -07:00
u32 new_state )
2006-10-04 02:15:52 -07:00
{
2006-10-04 02:15:56 -07:00
struct hlist_head * chain ;
struct hlist_node * pos ;
2006-10-04 02:15:55 -07:00
struct nsm_handle * nsm ;
2006-10-04 02:15:56 -07:00
struct nlm_host * host ;
2006-10-04 02:15:52 -07:00
2008-09-03 14:36:23 -04:00
nsm = nsm_find ( ( struct sockaddr * ) sin , sizeof ( * sin ) ,
hostname , hostname_len , 0 ) ;
2008-08-27 16:57:23 -04:00
if ( nsm = = NULL ) {
dprintk ( " lockd: never saw rebooted peer '%.*s' before \n " ,
hostname_len , hostname ) ;
2006-10-04 02:15:52 -07:00
return ;
2008-08-27 16:57:23 -04:00
}
dprintk ( " lockd: nlm_host_rebooted(%.*s, %s) \n " ,
hostname_len , hostname , nsm - > sm_addrbuf ) ;
2006-10-04 02:15:52 -07:00
2006-10-04 02:15:55 -07:00
/* When reclaiming locks on this peer, make sure that
* we set up a new notification */
nsm - > sm_monitored = 0 ;
/* Mark all hosts tied to this NSM state as having rebooted.
* We run the loop repeatedly , because we drop the host table
* lock for this .
* To avoid processing a host several times , we match the nsmstate .
*/
again : mutex_lock ( & nlm_host_mutex ) ;
2006-10-04 02:15:56 -07:00
for ( chain = nlm_hosts ; chain < nlm_hosts + NLM_HOST_NRHASH ; + + chain ) {
hlist_for_each_entry ( host , pos , chain , h_hash ) {
2006-10-04 02:15:55 -07:00
if ( host - > h_nsmhandle = = nsm
& & host - > h_nsmstate ! = new_state ) {
host - > h_nsmstate = new_state ;
host - > h_state + + ;
nlm_get_host ( host ) ;
mutex_unlock ( & nlm_host_mutex ) ;
if ( host - > h_server ) {
/* We're server for this guy, just ditch
* all the locks he held . */
nlmsvc_free_host_resources ( host ) ;
} else {
/* He's the server, initiate lock recovery. */
nlmclnt_recovery ( host ) ;
}
nlm_release_host ( host ) ;
goto again ;
}
}
2006-10-04 02:15:52 -07:00
}
2006-10-04 02:15:55 -07:00
mutex_unlock ( & nlm_host_mutex ) ;
2006-10-04 02:15:52 -07:00
}
2005-04-16 15:20:36 -07:00
/*
* Shut down the hosts module .
* Note that this routine is called only at server shutdown time .
*/
void
nlm_shutdown_hosts ( void )
{
2006-10-04 02:15:56 -07:00
struct hlist_head * chain ;
struct hlist_node * pos ;
2005-04-16 15:20:36 -07:00
struct nlm_host * host ;
dprintk ( " lockd: shutting down host module \n " ) ;
2006-03-26 01:37:12 -08:00
mutex_lock ( & nlm_host_mutex ) ;
2005-04-16 15:20:36 -07:00
/* First, make all hosts eligible for gc */
dprintk ( " lockd: nuking all hosts... \n " ) ;
2006-10-04 02:15:56 -07:00
for ( chain = nlm_hosts ; chain < nlm_hosts + NLM_HOST_NRHASH ; + + chain ) {
2008-01-29 10:30:55 -05:00
hlist_for_each_entry ( host , pos , chain , h_hash ) {
2005-04-16 15:20:36 -07:00
host - > h_expires = jiffies - 1 ;
2008-01-29 10:30:55 -05:00
if ( host - > h_rpcclnt ) {
rpc_shutdown_client ( host - > h_rpcclnt ) ;
host - > h_rpcclnt = NULL ;
}
}
2005-04-16 15:20:36 -07:00
}
/* Then, perform a garbage collection pass */
nlm_gc_hosts ( ) ;
2006-03-26 01:37:12 -08:00
mutex_unlock ( & nlm_host_mutex ) ;
2005-04-16 15:20:36 -07:00
/* complain if any hosts are left */
if ( nrhosts ) {
printk ( KERN_WARNING " lockd: couldn't shutdown host module! \n " ) ;
dprintk ( " lockd: %d hosts left: \n " , nrhosts ) ;
2006-10-04 02:15:56 -07:00
for ( chain = nlm_hosts ; chain < nlm_hosts + NLM_HOST_NRHASH ; + + chain ) {
hlist_for_each_entry ( host , pos , chain , h_hash ) {
2005-04-16 15:20:36 -07:00
dprintk ( " %s (cnt %d use %d exp %ld) \n " ,
host - > h_name , atomic_read ( & host - > h_count ) ,
host - > h_inuse , host - > h_expires ) ;
}
}
}
}
/*
* Garbage collect any unused NLM hosts .
* This GC combines reference counting for async operations with
* mark & sweep for resources held by remote clients .
*/
static void
nlm_gc_hosts ( void )
{
2006-10-04 02:15:56 -07:00
struct hlist_head * chain ;
struct hlist_node * pos , * next ;
struct nlm_host * host ;
2005-04-16 15:20:36 -07:00
dprintk ( " lockd: host garbage collection \n " ) ;
2006-10-04 02:15:56 -07:00
for ( chain = nlm_hosts ; chain < nlm_hosts + NLM_HOST_NRHASH ; + + chain ) {
hlist_for_each_entry ( host , pos , chain , h_hash )
2005-04-16 15:20:36 -07:00
host - > h_inuse = 0 ;
}
/* Mark all hosts that hold locks, blocks or shares */
nlmsvc_mark_resources ( ) ;
2006-10-04 02:15:56 -07:00
for ( chain = nlm_hosts ; chain < nlm_hosts + NLM_HOST_NRHASH ; + + chain ) {
hlist_for_each_entry_safe ( host , pos , next , chain , h_hash ) {
2005-04-16 15:20:36 -07:00
if ( atomic_read ( & host - > h_count ) | | host - > h_inuse
| | time_before ( jiffies , host - > h_expires ) ) {
dprintk ( " nlm_gc_hosts skipping %s (cnt %d use %d exp %ld) \n " ,
host - > h_name , atomic_read ( & host - > h_count ) ,
host - > h_inuse , host - > h_expires ) ;
continue ;
}
dprintk ( " lockd: delete host %s \n " , host - > h_name ) ;
2006-10-04 02:15:56 -07:00
hlist_del_init ( & host - > h_hash ) ;
2006-10-04 02:15:51 -07:00
2006-10-04 02:16:00 -07:00
nlm_destroy_host ( host ) ;
2005-04-16 15:20:36 -07:00
nrhosts - - ;
}
}
next_gc = jiffies + NLM_HOST_COLLECT ;
}
2006-10-04 02:15:53 -07:00
/*
* Manage NSM handles
*/
static LIST_HEAD ( nsm_handles ) ;
2008-02-20 15:40:15 -05:00
static DEFINE_SPINLOCK ( nsm_lock ) ;
2006-10-04 02:15:53 -07:00
2008-09-03 14:36:23 -04:00
static struct nsm_handle * nsm_find ( const struct sockaddr * sap ,
const size_t salen ,
2008-09-03 14:36:16 -04:00
const char * hostname ,
const size_t hostname_len ,
const int create )
2006-10-04 02:15:53 -07:00
{
struct nsm_handle * nsm = NULL ;
2008-02-20 15:27:31 -05:00
struct nsm_handle * pos ;
2006-10-04 02:15:53 -07:00
2008-09-03 14:36:23 -04:00
if ( ! sap )
2006-10-04 02:15:53 -07:00
return NULL ;
if ( hostname & & memchr ( hostname , ' / ' , hostname_len ) ! = NULL ) {
if ( printk_ratelimit ( ) ) {
printk ( KERN_WARNING " Invalid hostname \" %.*s \" "
" in NFS lock request \n " ,
2008-09-03 14:36:16 -04:00
( int ) hostname_len , hostname ) ;
2006-10-04 02:15:53 -07:00
}
return NULL ;
}
2008-02-20 15:40:15 -05:00
retry :
spin_lock ( & nsm_lock ) ;
2008-02-20 15:27:31 -05:00
list_for_each_entry ( pos , & nsm_handles , sm_link ) {
2006-10-04 02:15:53 -07:00
2006-10-04 02:16:01 -07:00
if ( hostname & & nsm_use_hostnames ) {
2008-02-20 15:27:31 -05:00
if ( strlen ( pos - > sm_name ) ! = hostname_len
| | memcmp ( pos - > sm_name , hostname , hostname_len ) )
2006-10-04 02:16:01 -07:00
continue ;
2008-09-03 14:36:23 -04:00
} else if ( ! nlm_cmp_addr ( nsm_addr ( pos ) , sap ) )
2006-10-04 02:15:53 -07:00
continue ;
2008-02-20 15:27:31 -05:00
atomic_inc ( & pos - > sm_count ) ;
2008-02-20 15:40:15 -05:00
kfree ( nsm ) ;
2008-02-20 15:27:31 -05:00
nsm = pos ;
2008-02-20 15:40:15 -05:00
goto found ;
2006-10-04 02:15:53 -07:00
}
2008-02-20 15:40:15 -05:00
if ( nsm ) {
list_add ( & nsm - > sm_link , & nsm_handles ) ;
goto found ;
2006-10-04 02:15:53 -07:00
}
2008-02-20 15:40:15 -05:00
spin_unlock ( & nsm_lock ) ;
if ( ! create )
return NULL ;
2006-10-04 02:15:53 -07:00
nsm = kzalloc ( sizeof ( * nsm ) + hostname_len + 1 , GFP_KERNEL ) ;
2008-02-20 15:27:31 -05:00
if ( nsm = = NULL )
2008-02-20 15:40:15 -05:00
return NULL ;
2008-09-03 14:36:23 -04:00
memcpy ( nsm_addr ( nsm ) , sap , salen ) ;
nsm - > sm_addrlen = salen ;
2008-02-20 15:27:31 -05:00
nsm - > sm_name = ( char * ) ( nsm + 1 ) ;
memcpy ( nsm - > sm_name , hostname , hostname_len ) ;
nsm - > sm_name [ hostname_len ] = ' \0 ' ;
2008-08-27 16:57:23 -04:00
nlm_display_address ( ( struct sockaddr * ) & nsm - > sm_addr ,
nsm - > sm_addrbuf , sizeof ( nsm - > sm_addrbuf ) ) ;
2008-02-20 15:27:31 -05:00
atomic_set ( & nsm - > sm_count , 1 ) ;
2008-02-20 15:40:15 -05:00
goto retry ;
2008-02-20 15:27:31 -05:00
2008-02-20 15:40:15 -05:00
found :
spin_unlock ( & nsm_lock ) ;
2006-10-04 02:15:53 -07:00
return nsm ;
}
/*
* Release an NSM handle
*/
void
nsm_release ( struct nsm_handle * nsm )
{
if ( ! nsm )
return ;
2008-02-20 15:40:15 -05:00
if ( atomic_dec_and_lock ( & nsm - > sm_count , & nsm_lock ) ) {
2008-02-20 14:02:47 -05:00
list_del ( & nsm - > sm_link ) ;
2008-02-20 15:40:15 -05:00
spin_unlock ( & nsm_lock ) ;
2008-02-20 14:02:47 -05:00
kfree ( nsm ) ;
2006-10-04 02:15:53 -07:00
}
}