2016-09-03 00:39:45 +03:00
/* AF_RXRPC sendmsg() implementation.
*
* Copyright ( C ) 2007 , 2016 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 Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/net.h>
# include <linux/gfp.h>
# include <linux/skbuff.h>
# include <linux/export.h>
# include <linux/circ_buf.h>
# include <net/sock.h>
# include <net/af_rxrpc.h>
# include "ar-internal.h"
2016-09-04 15:25:21 +03:00
enum rxrpc_command {
RXRPC_CMD_SEND_DATA , /* send data message */
RXRPC_CMD_SEND_ABORT , /* request abort generation */
RXRPC_CMD_ACCEPT , /* [server] accept incoming call */
RXRPC_CMD_REJECT_BUSY , /* [server] reject a call as busy */
} ;
2016-09-03 00:39:45 +03:00
/*
2016-09-03 00:39:45 +03:00
* wait for space to appear in the transmit / ACK window
* - caller holds the socket locked
2016-09-03 00:39:45 +03:00
*/
2016-09-03 00:39:45 +03:00
static int rxrpc_wait_for_tx_window ( struct rxrpc_sock * rx ,
struct rxrpc_call * call ,
long * timeo )
2016-09-03 00:39:45 +03:00
{
2016-09-03 00:39:45 +03:00
DECLARE_WAITQUEUE ( myself , current ) ;
int ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_enter ( " ,{%d},%ld " ,
CIRC_SPACE ( call - > acks_head , ACCESS_ONCE ( call - > acks_tail ) ,
call - > acks_winsz ) ,
* timeo ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
add_wait_queue ( & call - > waitq , & myself ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
for ( ; ; ) {
set_current_state ( TASK_INTERRUPTIBLE ) ;
ret = 0 ;
if ( CIRC_SPACE ( call - > acks_head , ACCESS_ONCE ( call - > acks_tail ) ,
call - > acks_winsz ) > 0 )
2016-09-03 00:39:45 +03:00
break ;
2016-09-03 00:39:45 +03:00
if ( signal_pending ( current ) ) {
ret = sock_intr_errno ( * timeo ) ;
2016-09-03 00:39:45 +03:00
break ;
}
2016-09-03 00:39:45 +03:00
release_sock ( & rx - > sk ) ;
* timeo = schedule_timeout ( * timeo ) ;
lock_sock ( & rx - > sk ) ;
2016-09-03 00:39:45 +03:00
}
2016-09-03 00:39:45 +03:00
remove_wait_queue ( & call - > waitq , & myself ) ;
set_current_state ( TASK_RUNNING ) ;
_leave ( " = %d " , ret ) ;
return ret ;
2016-09-03 00:39:45 +03:00
}
/*
2016-09-03 00:39:45 +03:00
* attempt to schedule an instant Tx resend
2016-09-03 00:39:45 +03:00
*/
2016-09-03 00:39:45 +03:00
static inline void rxrpc_instant_resend ( struct rxrpc_call * call )
2016-09-03 00:39:45 +03:00
{
2016-09-03 00:39:45 +03:00
read_lock_bh ( & call - > state_lock ) ;
if ( try_to_del_timer_sync ( & call - > resend_timer ) > = 0 ) {
2016-09-03 00:39:45 +03:00
clear_bit ( RXRPC_CALL_RUN_RTIMER , & call - > flags ) ;
2016-09-03 00:39:45 +03:00
if ( call - > state < RXRPC_CALL_COMPLETE & &
! test_and_set_bit ( RXRPC_CALL_EV_RESEND_TIMER , & call - > events ) )
rxrpc_queue_call ( call ) ;
2016-09-03 00:39:45 +03:00
}
2016-09-03 00:39:45 +03:00
read_unlock_bh ( & call - > state_lock ) ;
2016-09-03 00:39:45 +03:00
}
/*
2016-09-03 00:39:45 +03:00
* queue a packet for transmission , set the resend timer and attempt
* to send the packet immediately
2016-09-03 00:39:45 +03:00
*/
2016-09-03 00:39:45 +03:00
static void rxrpc_queue_packet ( struct rxrpc_call * call , struct sk_buff * skb ,
bool last )
2016-09-03 00:39:45 +03:00
{
2016-09-03 00:39:45 +03:00
struct rxrpc_skb_priv * sp = rxrpc_skb ( skb ) ;
int ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_net ( " queue skb %p [%d] " , skb , call - > acks_head ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
ASSERT ( call - > acks_window ! = NULL ) ;
call - > acks_window [ call - > acks_head ] = ( unsigned long ) skb ;
smp_wmb ( ) ;
call - > acks_head = ( call - > acks_head + 1 ) & ( call - > acks_winsz - 1 ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( last | | call - > state = = RXRPC_CALL_SERVER_ACK_REQUEST ) {
_debug ( " ________awaiting reply/ACK__________ " ) ;
write_lock_bh ( & call - > state_lock ) ;
switch ( call - > state ) {
case RXRPC_CALL_CLIENT_SEND_REQUEST :
call - > state = RXRPC_CALL_CLIENT_AWAIT_REPLY ;
break ;
case RXRPC_CALL_SERVER_ACK_REQUEST :
call - > state = RXRPC_CALL_SERVER_SEND_REPLY ;
if ( ! last )
break ;
case RXRPC_CALL_SERVER_SEND_REPLY :
call - > state = RXRPC_CALL_SERVER_AWAIT_ACK ;
break ;
default :
break ;
}
write_unlock_bh ( & call - > state_lock ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_proto ( " Tx DATA %%%u { #%u } " , sp - > hdr . serial , sp - > hdr . seq ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
sp - > need_resend = false ;
sp - > resend_at = jiffies + rxrpc_resend_timeout ;
if ( ! test_and_set_bit ( RXRPC_CALL_RUN_RTIMER , & call - > flags ) ) {
_debug ( " run timer " ) ;
call - > resend_timer . expires = sp - > resend_at ;
add_timer ( & call - > resend_timer ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* attempt to cancel the rx-ACK timer, deferring reply transmission if
* we ' re ACK ' ing the request phase of an incoming call */
ret = - EAGAIN ;
if ( try_to_del_timer_sync ( & call - > ack_timer ) > = 0 ) {
/* the packet may be freed by rxrpc_process_call() before this
* returns */
if ( rxrpc_is_client_call ( call ) )
rxrpc_expose_client_call ( call ) ;
ret = rxrpc_send_data_packet ( call - > conn , skb ) ;
_net ( " sent skb %p " , skb ) ;
} else {
_debug ( " failed to delete ACK timer " ) ;
}
if ( ret < 0 ) {
_debug ( " need instant resend %d " , ret ) ;
sp - > need_resend = true ;
rxrpc_instant_resend ( call ) ;
}
_leave ( " " ) ;
2016-09-03 00:39:45 +03:00
}
/*
2016-09-03 00:39:45 +03:00
* Convert a host - endian header into a network - endian header .
*/
static void rxrpc_insert_header ( struct sk_buff * skb )
{
struct rxrpc_wire_header whdr ;
struct rxrpc_skb_priv * sp = rxrpc_skb ( skb ) ;
whdr . epoch = htonl ( sp - > hdr . epoch ) ;
whdr . cid = htonl ( sp - > hdr . cid ) ;
whdr . callNumber = htonl ( sp - > hdr . callNumber ) ;
whdr . seq = htonl ( sp - > hdr . seq ) ;
whdr . serial = htonl ( sp - > hdr . serial ) ;
whdr . type = sp - > hdr . type ;
whdr . flags = sp - > hdr . flags ;
whdr . userStatus = sp - > hdr . userStatus ;
whdr . securityIndex = sp - > hdr . securityIndex ;
whdr . _rsvd = htons ( sp - > hdr . _rsvd ) ;
whdr . serviceId = htons ( sp - > hdr . serviceId ) ;
memcpy ( skb - > head , & whdr , sizeof ( whdr ) ) ;
}
/*
* send data through a socket
* - must be called in process context
2016-09-03 00:39:45 +03:00
* - caller holds the socket locked
*/
2016-09-03 00:39:45 +03:00
static int rxrpc_send_data ( struct rxrpc_sock * rx ,
struct rxrpc_call * call ,
struct msghdr * msg , size_t len )
2016-09-03 00:39:45 +03:00
{
2016-09-03 00:39:45 +03:00
struct rxrpc_skb_priv * sp ;
struct sk_buff * skb ;
struct sock * sk = & rx - > sk ;
long timeo ;
bool more ;
int ret , copied ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
timeo = sock_sndtimeo ( sk , msg - > msg_flags & MSG_DONTWAIT ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* this should be in poll */
sk_clear_bit ( SOCKWQ_ASYNC_NOSPACE , sk ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( sk - > sk_err | | ( sk - > sk_shutdown & SEND_SHUTDOWN ) )
return - EPIPE ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
more = msg - > msg_flags & MSG_MORE ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
skb = call - > tx_pending ;
call - > tx_pending = NULL ;
rxrpc_see_skb ( skb ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
copied = 0 ;
do {
if ( ! skb ) {
size_t size , chunk , max , space ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " alloc " ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( CIRC_SPACE ( call - > acks_head ,
ACCESS_ONCE ( call - > acks_tail ) ,
call - > acks_winsz ) < = 0 ) {
ret = - EAGAIN ;
if ( msg - > msg_flags & MSG_DONTWAIT )
goto maybe_error ;
ret = rxrpc_wait_for_tx_window ( rx , call ,
& timeo ) ;
if ( ret < 0 )
goto maybe_error ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
max = call - > conn - > params . peer - > maxdata ;
max - = call - > conn - > security_size ;
max & = ~ ( call - > conn - > size_align - 1UL ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
chunk = max ;
if ( chunk > msg_data_left ( msg ) & & ! more )
chunk = msg_data_left ( msg ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
space = chunk + call - > conn - > size_align ;
space & = ~ ( call - > conn - > size_align - 1UL ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
size = space + call - > conn - > header_size ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " SIZE: %zu/%zu/%zu " , chunk , space , size ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* create a buffer that we can retain until it's ACK'd */
skb = sock_alloc_send_skb (
sk , size , msg - > msg_flags & MSG_DONTWAIT , & ret ) ;
if ( ! skb )
goto maybe_error ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
rxrpc_new_skb ( skb ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " ALLOC SEND %p " , skb ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
ASSERTCMP ( skb - > mark , = = , 0 ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " HS: %u " , call - > conn - > header_size ) ;
skb_reserve ( skb , call - > conn - > header_size ) ;
skb - > len + = call - > conn - > header_size ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
sp = rxrpc_skb ( skb ) ;
sp - > remain = chunk ;
if ( sp - > remain > skb_tailroom ( skb ) )
sp - > remain = skb_tailroom ( skb ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_net ( " skb: hr %d, tr %d, hl %d, rm %d " ,
skb_headroom ( skb ) ,
skb_tailroom ( skb ) ,
skb_headlen ( skb ) ,
sp - > remain ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " append " ) ;
sp = rxrpc_skb ( skb ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* append next segment of data to the current buffer */
if ( msg_data_left ( msg ) > 0 ) {
int copy = skb_tailroom ( skb ) ;
ASSERTCMP ( copy , > , 0 ) ;
if ( copy > msg_data_left ( msg ) )
copy = msg_data_left ( msg ) ;
if ( copy > sp - > remain )
copy = sp - > remain ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " add " ) ;
ret = skb_add_data ( skb , & msg - > msg_iter , copy ) ;
_debug ( " added " ) ;
if ( ret < 0 )
goto efault ;
sp - > remain - = copy ;
skb - > mark + = copy ;
copied + = copy ;
2016-09-03 00:39:45 +03:00
}
2016-09-03 00:39:45 +03:00
/* check for the far side aborting the call or a network error
* occurring */
if ( call - > state = = RXRPC_CALL_COMPLETE )
goto call_terminated ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* add the packet to the send queue if it's now full */
if ( sp - > remain < = 0 | |
( msg_data_left ( msg ) = = 0 & & ! more ) ) {
struct rxrpc_connection * conn = call - > conn ;
uint32_t seq ;
size_t pad ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/* pad out if we're using security */
if ( conn - > security_ix ) {
pad = conn - > security_size + skb - > mark ;
pad = conn - > size_align - pad ;
pad & = conn - > size_align - 1 ;
_debug ( " pad %zu " , pad ) ;
if ( pad )
memset ( skb_put ( skb , pad ) , 0 , pad ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
seq = atomic_inc_return ( & call - > sequence ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
sp - > hdr . epoch = conn - > proto . epoch ;
sp - > hdr . cid = call - > cid ;
sp - > hdr . callNumber = call - > call_id ;
sp - > hdr . seq = seq ;
sp - > hdr . serial = atomic_inc_return ( & conn - > serial ) ;
sp - > hdr . type = RXRPC_PACKET_TYPE_DATA ;
sp - > hdr . userStatus = 0 ;
sp - > hdr . securityIndex = conn - > security_ix ;
sp - > hdr . _rsvd = 0 ;
sp - > hdr . serviceId = call - > service_id ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
sp - > hdr . flags = conn - > out_clientflag ;
if ( msg_data_left ( msg ) = = 0 & & ! more )
sp - > hdr . flags | = RXRPC_LAST_PACKET ;
else if ( CIRC_SPACE ( call - > acks_head ,
ACCESS_ONCE ( call - > acks_tail ) ,
call - > acks_winsz ) > 1 )
sp - > hdr . flags | = RXRPC_MORE_PACKETS ;
if ( more & & seq & 1 )
sp - > hdr . flags | = RXRPC_REQUEST_ACK ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
ret = conn - > security - > secure_packet (
call , skb , skb - > mark ,
skb - > head + sizeof ( struct rxrpc_wire_header ) ) ;
if ( ret < 0 )
goto out ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
rxrpc_insert_header ( skb ) ;
rxrpc_queue_packet ( call , skb , ! msg_data_left ( msg ) & & ! more ) ;
skb = NULL ;
}
} while ( msg_data_left ( msg ) > 0 ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
success :
ret = copied ;
out :
call - > tx_pending = skb ;
_leave ( " = %d " , ret ) ;
return ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
call_terminated :
rxrpc_free_skb ( skb ) ;
_leave ( " = %d " , - call - > error ) ;
return ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
maybe_error :
if ( copied )
goto success ;
goto out ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
efault :
ret = - EFAULT ;
goto out ;
2016-09-03 00:39:45 +03:00
}
/*
2016-09-03 00:39:45 +03:00
* extract control messages from the sendmsg ( ) control buffer
2016-09-03 00:39:45 +03:00
*/
2016-09-03 00:39:45 +03:00
static int rxrpc_sendmsg_cmsg ( struct msghdr * msg ,
unsigned long * user_call_ID ,
enum rxrpc_command * command ,
u32 * abort_code ,
bool * _exclusive )
2016-09-03 00:39:45 +03:00
{
2016-09-03 00:39:45 +03:00
struct cmsghdr * cmsg ;
bool got_user_ID = false ;
int len ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
* command = RXRPC_CMD_SEND_DATA ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( msg - > msg_controllen = = 0 )
return - EINVAL ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
for_each_cmsghdr ( cmsg , msg ) {
if ( ! CMSG_OK ( msg , cmsg ) )
return - EINVAL ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
len = cmsg - > cmsg_len - CMSG_ALIGN ( sizeof ( struct cmsghdr ) ) ;
_debug ( " CMSG %d, %d, %d " ,
cmsg - > cmsg_level , cmsg - > cmsg_type , len ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( cmsg - > cmsg_level ! = SOL_RXRPC )
continue ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
switch ( cmsg - > cmsg_type ) {
case RXRPC_USER_CALL_ID :
if ( msg - > msg_flags & MSG_CMSG_COMPAT ) {
if ( len ! = sizeof ( u32 ) )
return - EINVAL ;
* user_call_ID = * ( u32 * ) CMSG_DATA ( cmsg ) ;
} else {
if ( len ! = sizeof ( unsigned long ) )
return - EINVAL ;
* user_call_ID = * ( unsigned long * )
CMSG_DATA ( cmsg ) ;
}
_debug ( " User Call ID %lx " , * user_call_ID ) ;
got_user_ID = true ;
break ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
case RXRPC_ABORT :
if ( * command ! = RXRPC_CMD_SEND_DATA )
return - EINVAL ;
* command = RXRPC_CMD_SEND_ABORT ;
if ( len ! = sizeof ( * abort_code ) )
return - EINVAL ;
* abort_code = * ( unsigned int * ) CMSG_DATA ( cmsg ) ;
_debug ( " Abort %x " , * abort_code ) ;
if ( * abort_code = = 0 )
return - EINVAL ;
break ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
case RXRPC_ACCEPT :
if ( * command ! = RXRPC_CMD_SEND_DATA )
return - EINVAL ;
* command = RXRPC_CMD_ACCEPT ;
if ( len ! = 0 )
return - EINVAL ;
break ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
case RXRPC_EXCLUSIVE_CALL :
* _exclusive = true ;
if ( len ! = 0 )
return - EINVAL ;
break ;
default :
return - EINVAL ;
}
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( ! got_user_ID )
return - EINVAL ;
_leave ( " = 0 " ) ;
return 0 ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/*
* abort a call , sending an ABORT packet to the peer
*/
static void rxrpc_send_abort ( struct rxrpc_call * call , u32 abort_code )
{
if ( call - > state > = RXRPC_CALL_COMPLETE )
return ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
write_lock_bh ( & call - > state_lock ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( __rxrpc_abort_call ( call , abort_code , ECONNABORTED ) ) {
del_timer_sync ( & call - > resend_timer ) ;
del_timer_sync ( & call - > ack_timer ) ;
clear_bit ( RXRPC_CALL_EV_RESEND_TIMER , & call - > events ) ;
clear_bit ( RXRPC_CALL_EV_ACK , & call - > events ) ;
clear_bit ( RXRPC_CALL_RUN_RTIMER , & call - > flags ) ;
rxrpc_queue_call ( call ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
write_unlock_bh ( & call - > state_lock ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/*
* Create a new client call for sendmsg ( ) .
*/
static struct rxrpc_call *
rxrpc_new_client_call_for_sendmsg ( struct rxrpc_sock * rx , struct msghdr * msg ,
unsigned long user_call_ID , bool exclusive )
{
struct rxrpc_conn_parameters cp ;
struct rxrpc_call * call ;
struct key * key ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
DECLARE_SOCKADDR ( struct sockaddr_rxrpc * , srx , msg - > msg_name ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_enter ( " " ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( ! msg - > msg_name )
return ERR_PTR ( - EDESTADDRREQ ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
key = rx - > key ;
if ( key & & ! rx - > key - > payload . data [ 0 ] )
key = NULL ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
memset ( & cp , 0 , sizeof ( cp ) ) ;
cp . local = rx - > local ;
cp . key = rx - > key ;
cp . security_level = rx - > min_sec_level ;
cp . exclusive = rx - > exclusive | exclusive ;
cp . service_id = srx - > srx_service ;
call = rxrpc_new_client_call ( rx , & cp , srx , user_call_ID , GFP_KERNEL ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_leave ( " = %p \n " , call ) ;
return call ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/*
* send a message forming part of a client call through an RxRPC socket
* - caller holds the socket locked
* - the socket may be either a client socket or a server socket
*/
int rxrpc_do_sendmsg ( struct rxrpc_sock * rx , struct msghdr * msg , size_t len )
{
enum rxrpc_command cmd ;
struct rxrpc_call * call ;
unsigned long user_call_ID = 0 ;
bool exclusive = false ;
u32 abort_code = 0 ;
int ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_enter ( " " ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
ret = rxrpc_sendmsg_cmsg ( msg , & user_call_ID , & cmd , & abort_code ,
& exclusive ) ;
if ( ret < 0 )
return ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( cmd = = RXRPC_CMD_ACCEPT ) {
if ( rx - > sk . sk_state ! = RXRPC_SERVER_LISTENING )
return - EINVAL ;
call = rxrpc_accept_call ( rx , user_call_ID , NULL ) ;
if ( IS_ERR ( call ) )
return PTR_ERR ( call ) ;
rxrpc_put_call ( call ) ;
return 0 ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
call = rxrpc_find_call_by_user_ID ( rx , user_call_ID ) ;
if ( ! call ) {
if ( cmd ! = RXRPC_CMD_SEND_DATA )
return - EBADSLT ;
call = rxrpc_new_client_call_for_sendmsg ( rx , msg , user_call_ID ,
exclusive ) ;
if ( IS_ERR ( call ) )
return PTR_ERR ( call ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
rxrpc_see_call ( call ) ;
_debug ( " CALL %d USR %lx ST %d on CONN %p " ,
call - > debug_id , call - > user_call_ID , call - > state , call - > conn ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( call - > state > = RXRPC_CALL_COMPLETE ) {
/* it's too late for this call */
ret = - ESHUTDOWN ;
} else if ( cmd = = RXRPC_CMD_SEND_ABORT ) {
rxrpc_send_abort ( call , abort_code ) ;
ret = 0 ;
} else if ( cmd ! = RXRPC_CMD_SEND_DATA ) {
ret = - EINVAL ;
} else if ( rxrpc_is_client_call ( call ) & &
call - > state ! = RXRPC_CALL_CLIENT_SEND_REQUEST ) {
/* request phase complete for this client call */
ret = - EPROTO ;
} else if ( rxrpc_is_service_call ( call ) & &
call - > state ! = RXRPC_CALL_SERVER_ACK_REQUEST & &
call - > state ! = RXRPC_CALL_SERVER_SEND_REPLY ) {
/* Reply phase not begun or not complete for service call. */
ret = - EPROTO ;
} else {
ret = rxrpc_send_data ( rx , call , msg , len ) ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
rxrpc_put_call ( call ) ;
_leave ( " = %d " , ret ) ;
return ret ;
}
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/**
* rxrpc_kernel_send_data - Allow a kernel service to send data on a call
* @ sock : The socket the call is on
* @ call : The call to send data through
* @ msg : The data to send
* @ len : The amount of data to send
*
* Allow a kernel service to send data on a call . The call must be in an state
* appropriate to sending data . No control data should be supplied in @ msg ,
* nor should an address be supplied . MSG_MORE should be flagged if there ' s
* more data to come , otherwise this data will end the transmission phase .
*/
int rxrpc_kernel_send_data ( struct socket * sock , struct rxrpc_call * call ,
struct msghdr * msg , size_t len )
{
int ret ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_enter ( " {%d,%s}, " , call - > debug_id , rxrpc_call_states [ call - > state ] ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
ASSERTCMP ( msg - > msg_name , = = , NULL ) ;
ASSERTCMP ( msg - > msg_control , = = , NULL ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
lock_sock ( sock - > sk ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " CALL %d USR %lx ST %d on CONN %p " ,
call - > debug_id , call - > user_call_ID , call - > state , call - > conn ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
if ( call - > state > = RXRPC_CALL_COMPLETE ) {
ret = - ESHUTDOWN ; /* it's too late for this call */
} else if ( call - > state ! = RXRPC_CALL_CLIENT_SEND_REQUEST & &
call - > state ! = RXRPC_CALL_SERVER_ACK_REQUEST & &
call - > state ! = RXRPC_CALL_SERVER_SEND_REPLY ) {
ret = - EPROTO ; /* request phase complete for this client call */
} else {
ret = rxrpc_send_data ( rxrpc_sk ( sock - > sk ) , call , msg , len ) ;
}
release_sock ( sock - > sk ) ;
2016-09-03 00:39:45 +03:00
_leave ( " = %d " , ret ) ;
return ret ;
2016-09-03 00:39:45 +03:00
}
EXPORT_SYMBOL ( rxrpc_kernel_send_data ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
/**
* rxrpc_kernel_abort_call - Allow a kernel service to abort a call
* @ sock : The socket the call is on
* @ call : The call to be aborted
* @ abort_code : The abort code to stick into the ABORT packet
*
* Allow a kernel service to abort a call , if it ' s still in an abortable state .
*/
void rxrpc_kernel_abort_call ( struct socket * sock , struct rxrpc_call * call ,
u32 abort_code )
{
_enter ( " {%d},%d " , call - > debug_id , abort_code ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
lock_sock ( sock - > sk ) ;
2016-09-03 00:39:45 +03:00
2016-09-03 00:39:45 +03:00
_debug ( " CALL %d USR %lx ST %d on CONN %p " ,
call - > debug_id , call - > user_call_ID , call - > state , call - > conn ) ;
rxrpc_send_abort ( call , abort_code ) ;
release_sock ( sock - > sk ) ;
_leave ( " " ) ;
2016-09-03 00:39:45 +03:00
}
2016-09-03 00:39:45 +03:00
EXPORT_SYMBOL ( rxrpc_kernel_abort_call ) ;