2005-04-17 02:20:36 +04:00
/* connection.c: Rx connection routines
*
* Copyright ( C ) 2002 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <rxrpc/rxrpc.h>
# include <rxrpc/transport.h>
# include <rxrpc/peer.h>
# include <rxrpc/connection.h>
# include <rxrpc/call.h>
# include <rxrpc/message.h>
# include <linux/udp.h>
# include <linux/ip.h>
# include <net/sock.h>
# include <asm/uaccess.h>
# include "internal.h"
__RXACCT_DECL ( atomic_t rxrpc_connection_count ) ;
LIST_HEAD ( rxrpc_conns ) ;
DECLARE_RWSEM ( rxrpc_conns_sem ) ;
unsigned long rxrpc_conn_timeout = 60 * 60 ;
static void rxrpc_conn_do_timeout ( struct rxrpc_connection * conn ) ;
static void __rxrpc_conn_timeout ( rxrpc_timer_t * timer )
{
struct rxrpc_connection * conn =
list_entry ( timer , struct rxrpc_connection , timeout ) ;
_debug ( " Rx CONN TIMEOUT [%p{u=%d}] " , conn , atomic_read ( & conn - > usage ) ) ;
rxrpc_conn_do_timeout ( conn ) ;
}
static const struct rxrpc_timer_ops rxrpc_conn_timer_ops = {
. timed_out = __rxrpc_conn_timeout ,
} ;
/*****************************************************************************/
/*
* create a new connection record
*/
static inline int __rxrpc_create_connection ( struct rxrpc_peer * peer ,
struct rxrpc_connection * * _conn )
{
struct rxrpc_connection * conn ;
_enter ( " %p " , peer ) ;
/* allocate and initialise a connection record */
2006-07-22 01:51:30 +04:00
conn = kzalloc ( sizeof ( struct rxrpc_connection ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! conn ) {
_leave ( " = -ENOMEM " ) ;
return - ENOMEM ;
}
atomic_set ( & conn - > usage , 1 ) ;
INIT_LIST_HEAD ( & conn - > link ) ;
INIT_LIST_HEAD ( & conn - > id_link ) ;
init_waitqueue_head ( & conn - > chanwait ) ;
spin_lock_init ( & conn - > lock ) ;
rxrpc_timer_init ( & conn - > timeout , & rxrpc_conn_timer_ops ) ;
do_gettimeofday ( & conn - > atime ) ;
conn - > mtu_size = 1024 ;
conn - > peer = peer ;
conn - > trans = peer - > trans ;
__RXACCT ( atomic_inc ( & rxrpc_connection_count ) ) ;
* _conn = conn ;
_leave ( " = 0 (%p) " , conn ) ;
return 0 ;
} /* end __rxrpc_create_connection() */
/*****************************************************************************/
/*
* create a new connection record for outgoing connections
*/
int rxrpc_create_connection ( struct rxrpc_transport * trans ,
__be16 port ,
__be32 addr ,
uint16_t service_id ,
void * security ,
struct rxrpc_connection * * _conn )
{
struct rxrpc_connection * candidate , * conn ;
struct rxrpc_peer * peer ;
struct list_head * _p ;
__be32 connid ;
int ret ;
_enter ( " %p{%hu},%u,%hu " , trans , trans - > port , ntohs ( port ) , service_id ) ;
/* get a peer record */
ret = rxrpc_peer_lookup ( trans , addr , & peer ) ;
if ( ret < 0 ) {
_leave ( " = %d " , ret ) ;
return ret ;
}
/* allocate and initialise a connection record */
ret = __rxrpc_create_connection ( peer , & candidate ) ;
if ( ret < 0 ) {
rxrpc_put_peer ( peer ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}
/* fill in the specific bits */
candidate - > addr . sin_family = AF_INET ;
candidate - > addr . sin_port = port ;
candidate - > addr . sin_addr . s_addr = addr ;
candidate - > in_epoch = rxrpc_epoch ;
candidate - > out_epoch = rxrpc_epoch ;
candidate - > in_clientflag = 0 ;
candidate - > out_clientflag = RXRPC_CLIENT_INITIATED ;
candidate - > service_id = htons ( service_id ) ;
/* invent a unique connection ID */
write_lock ( & peer - > conn_idlock ) ;
try_next_id :
connid = htonl ( peer - > conn_idcounter & RXRPC_CIDMASK ) ;
peer - > conn_idcounter + = RXRPC_MAXCALLS ;
list_for_each ( _p , & peer - > conn_idlist ) {
conn = list_entry ( _p , struct rxrpc_connection , id_link ) ;
if ( connid = = conn - > conn_id )
goto try_next_id ;
if ( connid > conn - > conn_id )
break ;
}
_debug ( " selected candidate conn ID %x.%u " ,
ntohl ( peer - > addr . s_addr ) , ntohl ( connid ) ) ;
candidate - > conn_id = connid ;
list_add_tail ( & candidate - > id_link , _p ) ;
write_unlock ( & peer - > conn_idlock ) ;
/* attach to peer */
candidate - > peer = peer ;
write_lock ( & peer - > conn_lock ) ;
/* search the peer's transport graveyard list */
spin_lock ( & peer - > conn_gylock ) ;
list_for_each ( _p , & peer - > conn_graveyard ) {
conn = list_entry ( _p , struct rxrpc_connection , link ) ;
if ( conn - > addr . sin_port = = candidate - > addr . sin_port & &
conn - > security_ix = = candidate - > security_ix & &
2007-02-09 17:25:14 +03:00
conn - > service_id = = candidate - > service_id & &
2005-04-17 02:20:36 +04:00
conn - > in_clientflag = = 0 )
goto found_in_graveyard ;
}
spin_unlock ( & peer - > conn_gylock ) ;
/* pick the new candidate */
_debug ( " created connection: {%08x} [out] " , ntohl ( candidate - > conn_id ) ) ;
atomic_inc ( & peer - > conn_count ) ;
conn = candidate ;
candidate = NULL ;
make_active :
list_add_tail ( & conn - > link , & peer - > conn_active ) ;
write_unlock ( & peer - > conn_lock ) ;
if ( candidate ) {
write_lock ( & peer - > conn_idlock ) ;
list_del ( & candidate - > id_link ) ;
write_unlock ( & peer - > conn_idlock ) ;
__RXACCT ( atomic_dec ( & rxrpc_connection_count ) ) ;
kfree ( candidate ) ;
}
else {
down_write ( & rxrpc_conns_sem ) ;
list_add_tail ( & conn - > proc_link , & rxrpc_conns ) ;
up_write ( & rxrpc_conns_sem ) ;
}
* _conn = conn ;
_leave ( " = 0 (%p) " , conn ) ;
return 0 ;
/* handle resurrecting a connection from the graveyard */
found_in_graveyard :
_debug ( " resurrecting connection: {%08x} [out] " , ntohl ( conn - > conn_id ) ) ;
rxrpc_get_connection ( conn ) ;
rxrpc_krxtimod_del_timer ( & conn - > timeout ) ;
list_del_init ( & conn - > link ) ;
spin_unlock ( & peer - > conn_gylock ) ;
goto make_active ;
} /* end rxrpc_create_connection() */
/*****************************************************************************/
/*
* lookup the connection for an incoming packet
* - create a new connection record for unrecorded incoming connections
*/
int rxrpc_connection_lookup ( struct rxrpc_peer * peer ,
struct rxrpc_message * msg ,
struct rxrpc_connection * * _conn )
{
struct rxrpc_connection * conn , * candidate = NULL ;
struct list_head * _p ;
2006-01-11 00:07:44 +03:00
struct sk_buff * pkt = msg - > pkt ;
2005-04-17 02:20:36 +04:00
int ret , fresh = 0 ;
__be32 x_epoch , x_connid ;
__be16 x_port , x_servid ;
__u32 x_secix ;
u8 x_clflag ;
_enter ( " %p{{%hu}},%u,%hu " ,
peer ,
peer - > trans - > port ,
2006-01-11 00:07:44 +03:00
ntohs ( pkt - > h . uh - > source ) ,
2005-04-17 02:20:36 +04:00
ntohs ( msg - > hdr . serviceId ) ) ;
2006-01-11 00:07:44 +03:00
x_port = pkt - > h . uh - > source ;
2005-04-17 02:20:36 +04:00
x_epoch = msg - > hdr . epoch ;
x_clflag = msg - > hdr . flags & RXRPC_CLIENT_INITIATED ;
x_connid = htonl ( ntohl ( msg - > hdr . cid ) & RXRPC_CIDMASK ) ;
x_servid = msg - > hdr . serviceId ;
x_secix = msg - > hdr . securityIndex ;
/* [common case] search the transport's active list first */
read_lock ( & peer - > conn_lock ) ;
list_for_each ( _p , & peer - > conn_active ) {
conn = list_entry ( _p , struct rxrpc_connection , link ) ;
if ( conn - > addr . sin_port = = x_port & &
conn - > in_epoch = = x_epoch & &
conn - > conn_id = = x_connid & &
conn - > security_ix = = x_secix & &
2007-02-09 17:25:14 +03:00
conn - > service_id = = x_servid & &
2005-04-17 02:20:36 +04:00
conn - > in_clientflag = = x_clflag )
goto found_active ;
}
read_unlock ( & peer - > conn_lock ) ;
2007-02-09 17:25:14 +03:00
/* [uncommon case] not active
2005-04-17 02:20:36 +04:00
* - create a candidate for a new record if an inbound connection
* - only examine the graveyard for an outbound connection
*/
if ( x_clflag ) {
ret = __rxrpc_create_connection ( peer , & candidate ) ;
if ( ret < 0 ) {
_leave ( " = %d " , ret ) ;
return ret ;
}
/* fill in the specifics */
candidate - > addr . sin_family = AF_INET ;
candidate - > addr . sin_port = x_port ;
2006-01-11 00:07:44 +03:00
candidate - > addr . sin_addr . s_addr = pkt - > nh . iph - > saddr ;
2005-04-17 02:20:36 +04:00
candidate - > in_epoch = x_epoch ;
candidate - > out_epoch = x_epoch ;
candidate - > in_clientflag = RXRPC_CLIENT_INITIATED ;
candidate - > out_clientflag = 0 ;
candidate - > conn_id = x_connid ;
candidate - > service_id = x_servid ;
candidate - > security_ix = x_secix ;
}
/* search the active list again, just in case it appeared whilst we
* were busy */
write_lock ( & peer - > conn_lock ) ;
list_for_each ( _p , & peer - > conn_active ) {
conn = list_entry ( _p , struct rxrpc_connection , link ) ;
if ( conn - > addr . sin_port = = x_port & &
conn - > in_epoch = = x_epoch & &
conn - > conn_id = = x_connid & &
conn - > security_ix = = x_secix & &
2007-02-09 17:25:14 +03:00
conn - > service_id = = x_servid & &
2005-04-17 02:20:36 +04:00
conn - > in_clientflag = = x_clflag )
goto found_active_second_chance ;
}
/* search the transport's graveyard list */
spin_lock ( & peer - > conn_gylock ) ;
list_for_each ( _p , & peer - > conn_graveyard ) {
conn = list_entry ( _p , struct rxrpc_connection , link ) ;
if ( conn - > addr . sin_port = = x_port & &
conn - > in_epoch = = x_epoch & &
conn - > conn_id = = x_connid & &
conn - > security_ix = = x_secix & &
2007-02-09 17:25:14 +03:00
conn - > service_id = = x_servid & &
2005-04-17 02:20:36 +04:00
conn - > in_clientflag = = x_clflag )
goto found_in_graveyard ;
}
spin_unlock ( & peer - > conn_gylock ) ;
/* outbound connections aren't created here */
if ( ! x_clflag ) {
write_unlock ( & peer - > conn_lock ) ;
_leave ( " = -ENOENT " ) ;
return - ENOENT ;
}
/* we can now add the new candidate to the list */
_debug ( " created connection: {%08x} [in] " , ntohl ( candidate - > conn_id ) ) ;
rxrpc_get_peer ( peer ) ;
conn = candidate ;
candidate = NULL ;
atomic_inc ( & peer - > conn_count ) ;
fresh = 1 ;
make_active :
list_add_tail ( & conn - > link , & peer - > conn_active ) ;
success_uwfree :
write_unlock ( & peer - > conn_lock ) ;
if ( candidate ) {
write_lock ( & peer - > conn_idlock ) ;
list_del ( & candidate - > id_link ) ;
write_unlock ( & peer - > conn_idlock ) ;
__RXACCT ( atomic_dec ( & rxrpc_connection_count ) ) ;
kfree ( candidate ) ;
}
if ( fresh ) {
down_write ( & rxrpc_conns_sem ) ;
list_add_tail ( & conn - > proc_link , & rxrpc_conns ) ;
up_write ( & rxrpc_conns_sem ) ;
}
success :
* _conn = conn ;
_leave ( " = 0 (%p) " , conn ) ;
return 0 ;
/* handle the connection being found in the active list straight off */
found_active :
rxrpc_get_connection ( conn ) ;
read_unlock ( & peer - > conn_lock ) ;
goto success ;
/* handle resurrecting a connection from the graveyard */
found_in_graveyard :
_debug ( " resurrecting connection: {%08x} [in] " , ntohl ( conn - > conn_id ) ) ;
rxrpc_get_peer ( peer ) ;
rxrpc_get_connection ( conn ) ;
rxrpc_krxtimod_del_timer ( & conn - > timeout ) ;
list_del_init ( & conn - > link ) ;
spin_unlock ( & peer - > conn_gylock ) ;
goto make_active ;
/* handle finding the connection on the second time through the active
* list */
found_active_second_chance :
rxrpc_get_connection ( conn ) ;
goto success_uwfree ;
} /* end rxrpc_connection_lookup() */
/*****************************************************************************/
/*
* finish using a connection record
* - it will be transferred to the peer ' s connection graveyard when refcount
* reaches 0
*/
void rxrpc_put_connection ( struct rxrpc_connection * conn )
{
struct rxrpc_peer * peer ;
if ( ! conn )
return ;
_enter ( " %p{u=%d p=%hu} " ,
conn , atomic_read ( & conn - > usage ) , ntohs ( conn - > addr . sin_port ) ) ;
peer = conn - > peer ;
spin_lock ( & peer - > conn_gylock ) ;
/* sanity check */
if ( atomic_read ( & conn - > usage ) < = 0 )
BUG ( ) ;
if ( likely ( ! atomic_dec_and_test ( & conn - > usage ) ) ) {
spin_unlock ( & peer - > conn_gylock ) ;
_leave ( " " ) ;
return ;
}
/* move to graveyard queue */
_debug ( " burying connection: {%08x} " , ntohl ( conn - > conn_id ) ) ;
2006-06-26 11:24:40 +04:00
list_move_tail ( & conn - > link , & peer - > conn_graveyard ) ;
2005-04-17 02:20:36 +04:00
rxrpc_krxtimod_add_timer ( & conn - > timeout , rxrpc_conn_timeout * HZ ) ;
spin_unlock ( & peer - > conn_gylock ) ;
rxrpc_put_peer ( conn - > peer ) ;
_leave ( " [killed] " ) ;
} /* end rxrpc_put_connection() */
/*****************************************************************************/
/*
* free a connection record
*/
static void rxrpc_conn_do_timeout ( struct rxrpc_connection * conn )
{
struct rxrpc_peer * peer ;
_enter ( " %p{u=%d p=%hu} " ,
conn , atomic_read ( & conn - > usage ) , ntohs ( conn - > addr . sin_port ) ) ;
peer = conn - > peer ;
if ( atomic_read ( & conn - > usage ) < 0 )
BUG ( ) ;
/* remove from graveyard if still dead */
spin_lock ( & peer - > conn_gylock ) ;
if ( atomic_read ( & conn - > usage ) = = 0 ) {
list_del_init ( & conn - > link ) ;
}
else {
conn = NULL ;
}
spin_unlock ( & peer - > conn_gylock ) ;
if ( ! conn ) {
_leave ( " " ) ;
return ; /* resurrected */
}
_debug ( " --- Destroying Connection %p{%08x} --- " ,
conn , ntohl ( conn - > conn_id ) ) ;
down_write ( & rxrpc_conns_sem ) ;
list_del ( & conn - > proc_link ) ;
up_write ( & rxrpc_conns_sem ) ;
write_lock ( & peer - > conn_idlock ) ;
list_del ( & conn - > id_link ) ;
write_unlock ( & peer - > conn_idlock ) ;
__RXACCT ( atomic_dec ( & rxrpc_connection_count ) ) ;
kfree ( conn ) ;
/* if the graveyard is now empty, wake up anyone waiting for that */
if ( atomic_dec_and_test ( & peer - > conn_count ) )
wake_up ( & peer - > conn_gy_waitq ) ;
_leave ( " [destroyed] " ) ;
} /* end rxrpc_conn_do_timeout() */
/*****************************************************************************/
/*
* clear all connection records from a peer endpoint
*/
void rxrpc_conn_clearall ( struct rxrpc_peer * peer )
{
DECLARE_WAITQUEUE ( myself , current ) ;
struct rxrpc_connection * conn ;
int err ;
_enter ( " %p " , peer ) ;
/* there shouldn't be any active conns remaining */
if ( ! list_empty ( & peer - > conn_active ) )
BUG ( ) ;
/* manually timeout all conns in the graveyard */
spin_lock ( & peer - > conn_gylock ) ;
while ( ! list_empty ( & peer - > conn_graveyard ) ) {
conn = list_entry ( peer - > conn_graveyard . next ,
struct rxrpc_connection , link ) ;
err = rxrpc_krxtimod_del_timer ( & conn - > timeout ) ;
spin_unlock ( & peer - > conn_gylock ) ;
if ( err = = 0 )
rxrpc_conn_do_timeout ( conn ) ;
spin_lock ( & peer - > conn_gylock ) ;
}
spin_unlock ( & peer - > conn_gylock ) ;
/* wait for the the conn graveyard to be completely cleared */
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
add_wait_queue ( & peer - > conn_gy_waitq , & myself ) ;
while ( atomic_read ( & peer - > conn_count ) ! = 0 ) {
schedule ( ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
}
remove_wait_queue ( & peer - > conn_gy_waitq , & myself ) ;
set_current_state ( TASK_RUNNING ) ;
_leave ( " " ) ;
} /* end rxrpc_conn_clearall() */
/*****************************************************************************/
/*
* allocate and prepare a message for sending out through the transport
* endpoint
*/
int rxrpc_conn_newmsg ( struct rxrpc_connection * conn ,
struct rxrpc_call * call ,
uint8_t type ,
int dcount ,
struct kvec diov [ ] ,
2005-10-07 10:46:04 +04:00
gfp_t alloc_flags ,
2005-04-17 02:20:36 +04:00
struct rxrpc_message * * _msg )
{
struct rxrpc_message * msg ;
int loop ;
_enter ( " %p{%d},%p,%u " , conn , ntohs ( conn - > addr . sin_port ) , call , type ) ;
if ( dcount > 3 ) {
_leave ( " = -EINVAL " ) ;
return - EINVAL ;
}
2006-07-22 01:51:30 +04:00
msg = kzalloc ( sizeof ( struct rxrpc_message ) , alloc_flags ) ;
2005-04-17 02:20:36 +04:00
if ( ! msg ) {
_leave ( " = -ENOMEM " ) ;
return - ENOMEM ;
}
atomic_set ( & msg - > usage , 1 ) ;
INIT_LIST_HEAD ( & msg - > link ) ;
msg - > state = RXRPC_MSG_PREPARED ;
msg - > hdr . epoch = conn - > out_epoch ;
msg - > hdr . cid = conn - > conn_id | ( call ? call - > chan_ix : 0 ) ;
msg - > hdr . callNumber = call ? call - > call_id : 0 ;
msg - > hdr . type = type ;
msg - > hdr . flags = conn - > out_clientflag ;
msg - > hdr . securityIndex = conn - > security_ix ;
msg - > hdr . serviceId = conn - > service_id ;
/* generate sequence numbers for data packets */
if ( call ) {
switch ( type ) {
case RXRPC_PACKET_TYPE_DATA :
msg - > seq = + + call - > snd_seq_count ;
msg - > hdr . seq = htonl ( msg - > seq ) ;
break ;
case RXRPC_PACKET_TYPE_ACK :
/* ACK sequence numbers are complicated. The following
* may be wrong :
* - jumbo packet ACKs should have a seq number
* - normal ACKs should not
*/
default :
break ;
}
}
msg - > dcount = dcount + 1 ;
msg - > dsize = sizeof ( msg - > hdr ) ;
msg - > data [ 0 ] . iov_len = sizeof ( msg - > hdr ) ;
msg - > data [ 0 ] . iov_base = & msg - > hdr ;
for ( loop = 0 ; loop < dcount ; loop + + ) {
msg - > dsize + = diov [ loop ] . iov_len ;
msg - > data [ loop + 1 ] . iov_len = diov [ loop ] . iov_len ;
msg - > data [ loop + 1 ] . iov_base = diov [ loop ] . iov_base ;
}
__RXACCT ( atomic_inc ( & rxrpc_message_count ) ) ;
* _msg = msg ;
_leave ( " = 0 (%p) #%d " , msg , atomic_read ( & rxrpc_message_count ) ) ;
return 0 ;
} /* end rxrpc_conn_newmsg() */
/*****************************************************************************/
/*
* free a message
*/
void __rxrpc_put_message ( struct rxrpc_message * msg )
{
int loop ;
_enter ( " %p #%d " , msg , atomic_read ( & rxrpc_message_count ) ) ;
if ( msg - > pkt )
kfree_skb ( msg - > pkt ) ;
rxrpc_put_connection ( msg - > conn ) ;
for ( loop = 0 ; loop < 8 ; loop + + )
if ( test_bit ( loop , & msg - > dfree ) )
kfree ( msg - > data [ loop ] . iov_base ) ;
__RXACCT ( atomic_dec ( & rxrpc_message_count ) ) ;
kfree ( msg ) ;
_leave ( " " ) ;
} /* end __rxrpc_put_message() */
/*****************************************************************************/
/*
* send a message out through the transport endpoint
*/
int rxrpc_conn_sendmsg ( struct rxrpc_connection * conn ,
struct rxrpc_message * msg )
{
struct msghdr msghdr ;
int ret ;
_enter ( " %p{%d} " , conn , ntohs ( conn - > addr . sin_port ) ) ;
/* fill in some fields in the header */
spin_lock ( & conn - > lock ) ;
msg - > hdr . serial = htonl ( + + conn - > serial_counter ) ;
msg - > rttdone = 0 ;
spin_unlock ( & conn - > lock ) ;
/* set up the message to be transmitted */
msghdr . msg_name = & conn - > addr ;
msghdr . msg_namelen = sizeof ( conn - > addr ) ;
msghdr . msg_control = NULL ;
msghdr . msg_controllen = 0 ;
msghdr . msg_flags = MSG_CONFIRM | MSG_DONTWAIT ;
_net ( " Sending message type %d of %Zd bytes to %08x:%d " ,
msg - > hdr . type ,
msg - > dsize ,
ntohl ( conn - > addr . sin_addr . s_addr ) ,
ntohs ( conn - > addr . sin_port ) ) ;
/* send the message */
ret = kernel_sendmsg ( conn - > trans - > socket , & msghdr ,
msg - > data , msg - > dcount , msg - > dsize ) ;
if ( ret < 0 ) {
msg - > state = RXRPC_MSG_ERROR ;
} else {
msg - > state = RXRPC_MSG_SENT ;
ret = 0 ;
spin_lock ( & conn - > lock ) ;
do_gettimeofday ( & conn - > atime ) ;
msg - > stamp = conn - > atime ;
spin_unlock ( & conn - > lock ) ;
}
_leave ( " = %d " , ret ) ;
return ret ;
} /* end rxrpc_conn_sendmsg() */
/*****************************************************************************/
/*
* deal with a subsequent call packet
*/
int rxrpc_conn_receive_call_packet ( struct rxrpc_connection * conn ,
struct rxrpc_call * call ,
struct rxrpc_message * msg )
{
struct rxrpc_message * pmsg ;
2006-01-11 00:07:44 +03:00
struct dst_entry * dst ;
2005-04-17 02:20:36 +04:00
struct list_head * _p ;
unsigned cix , seq ;
int ret = 0 ;
_enter ( " %p,%p,%p " , conn , call , msg ) ;
if ( ! call ) {
cix = ntohl ( msg - > hdr . cid ) & RXRPC_CHANNELMASK ;
spin_lock ( & conn - > lock ) ;
call = conn - > channels [ cix ] ;
if ( ! call | | call - > call_id ! = msg - > hdr . callNumber ) {
spin_unlock ( & conn - > lock ) ;
rxrpc_trans_immediate_abort ( conn - > trans , msg , - ENOENT ) ;
goto out ;
}
else {
rxrpc_get_call ( call ) ;
spin_unlock ( & conn - > lock ) ;
}
}
else {
rxrpc_get_call ( call ) ;
}
_proto ( " Received packet %%%u [%u] on call %hu:%u:%u " ,
ntohl ( msg - > hdr . serial ) ,
ntohl ( msg - > hdr . seq ) ,
ntohs ( msg - > hdr . serviceId ) ,
ntohl ( conn - > conn_id ) ,
ntohl ( call - > call_id ) ) ;
call - > pkt_rcv_count + + ;
2006-01-11 00:07:44 +03:00
dst = msg - > pkt - > dst ;
if ( dst & & dst - > dev )
2005-04-17 02:20:36 +04:00
conn - > peer - > if_mtu =
2006-01-11 00:07:44 +03:00
dst - > dev - > mtu - dst - > dev - > hard_header_len ;
2005-04-17 02:20:36 +04:00
/* queue on the call in seq order */
rxrpc_get_message ( msg ) ;
seq = msg - > seq ;
spin_lock ( & call - > lock ) ;
list_for_each ( _p , & call - > rcv_receiveq ) {
pmsg = list_entry ( _p , struct rxrpc_message , link ) ;
if ( pmsg - > seq > seq )
break ;
}
list_add_tail ( & msg - > link , _p ) ;
/* reset the activity timeout */
call - > flags | = RXRPC_CALL_RCV_PKT ;
mod_timer ( & call - > rcv_timeout , jiffies + rxrpc_call_rcv_timeout * HZ ) ;
spin_unlock ( & call - > lock ) ;
rxrpc_krxiod_queue_call ( call ) ;
rxrpc_put_call ( call ) ;
out :
_leave ( " = %d " , ret ) ;
return ret ;
} /* end rxrpc_conn_receive_call_packet() */
/*****************************************************************************/
/*
* handle an ICMP error being applied to a connection
*/
void rxrpc_conn_handle_error ( struct rxrpc_connection * conn ,
int local , int errno )
{
struct rxrpc_call * calls [ 4 ] ;
int loop ;
_enter ( " %p{%d},%d " , conn , ntohs ( conn - > addr . sin_port ) , errno ) ;
/* get a ref to all my calls in one go */
memset ( calls , 0 , sizeof ( calls ) ) ;
spin_lock ( & conn - > lock ) ;
for ( loop = 3 ; loop > = 0 ; loop - - ) {
if ( conn - > channels [ loop ] ) {
calls [ loop ] = conn - > channels [ loop ] ;
rxrpc_get_call ( calls [ loop ] ) ;
}
}
spin_unlock ( & conn - > lock ) ;
/* now kick them all */
for ( loop = 3 ; loop > = 0 ; loop - - ) {
if ( calls [ loop ] ) {
rxrpc_call_handle_error ( calls [ loop ] , local , errno ) ;
rxrpc_put_call ( calls [ loop ] ) ;
}
}
_leave ( " " ) ;
} /* end rxrpc_conn_handle_error() */