2005-04-17 02:20:36 +04:00
/* net/atm/svc.c - ATM SVC sockets */
/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
2010-01-26 14:40:00 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
2005-04-17 02:20:36 +04:00
# include <linux/string.h>
# include <linux/net.h> /* struct socket, struct proto_ops */
# include <linux/errno.h> /* error codes */
# include <linux/kernel.h> /* printk */
# include <linux/skbuff.h>
# include <linux/wait.h>
# include <linux/sched.h> /* jiffies and HZ */
# include <linux/fcntl.h> /* O_NONBLOCK */
# include <linux/init.h>
# include <linux/atm.h> /* ATM stuff */
# include <linux/atmsap.h>
# include <linux/atmsvc.h>
# include <linux/atmdev.h>
# include <linux/bitops.h>
# include <net/sock.h> /* for sock_no_* */
2010-01-26 14:40:18 +03:00
# include <linux/uaccess.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include "resources.h"
# include "common.h" /* common for PVCs and SVCs */
# include "signaling.h"
# include "addr.h"
2010-01-26 14:40:18 +03:00
static int svc_create ( struct net * net , struct socket * sock , int protocol ,
int kern ) ;
2005-04-17 02:20:36 +04:00
/*
* Note : since all this is still nicely synchronized with the signaling demon ,
* there ' s no need to protect sleep loops with clis . If signaling is
* moved into the kernel , that would change .
*/
2010-01-26 14:40:18 +03:00
static int svc_shutdown ( struct socket * sock , int how )
2005-04-17 02:20:36 +04:00
{
return 0 ;
}
static void svc_disconnect ( struct atm_vcc * vcc )
{
DEFINE_WAIT ( wait ) ;
struct sk_buff * skb ;
struct sock * sk = sk_atm ( vcc ) ;
2010-01-26 14:40:18 +03:00
pr_debug ( " %p \n " , vcc ) ;
if ( test_bit ( ATM_VF_REGIS , & vcc - > flags ) ) {
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2010-01-26 14:40:18 +03:00
sigd_enq ( vcc , as_close , NULL , NULL , NULL ) ;
while ( ! test_bit ( ATM_VF_RELEASED , & vcc - > flags ) & & sigd ) {
2005-04-17 02:20:36 +04:00
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
}
/* beware - socket is still in use by atmsigd until the last
as_indicate has been answered */
while ( ( skb = skb_dequeue ( & sk - > sk_receive_queue ) ) ! = NULL ) {
atm_return ( vcc , skb - > truesize ) ;
2007-08-29 02:22:09 +04:00
pr_debug ( " LISTEN REL \n " ) ;
2010-01-26 14:40:18 +03:00
sigd_enq2 ( NULL , as_reject , vcc , NULL , NULL , & vcc - > qos , 0 ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb ( skb ) ;
}
clear_bit ( ATM_VF_REGIS , & vcc - > flags ) ;
/* ... may retry later */
}
static int svc_release ( struct socket * sock )
{
struct sock * sk = sock - > sk ;
struct atm_vcc * vcc ;
2010-01-26 14:40:18 +03:00
if ( sk ) {
2005-04-17 02:20:36 +04:00
vcc = ATM_SD ( sock ) ;
2010-01-26 14:40:00 +03:00
pr_debug ( " %p \n " , vcc ) ;
2005-04-17 02:20:36 +04:00
clear_bit ( ATM_VF_READY , & vcc - > flags ) ;
2010-01-26 14:40:18 +03:00
/*
* VCC pointer is used as a reference ,
* so we must not free it ( thereby subjecting it to re - use )
* before all pending connections are closed
*/
2005-04-17 02:20:36 +04:00
svc_disconnect ( vcc ) ;
vcc_release ( sock ) ;
}
return 0 ;
}
2010-01-26 14:40:18 +03:00
static int svc_bind ( struct socket * sock , struct sockaddr * sockaddr ,
int sockaddr_len )
2005-04-17 02:20:36 +04:00
{
DEFINE_WAIT ( wait ) ;
struct sock * sk = sock - > sk ;
struct sockaddr_atmsvc * addr ;
struct atm_vcc * vcc ;
int error ;
if ( sockaddr_len ! = sizeof ( struct sockaddr_atmsvc ) )
return - EINVAL ;
lock_sock ( sk ) ;
if ( sock - > state = = SS_CONNECTED ) {
error = - EISCONN ;
goto out ;
}
if ( sock - > state ! = SS_UNCONNECTED ) {
error = - EINVAL ;
goto out ;
}
vcc = ATM_SD ( sock ) ;
addr = ( struct sockaddr_atmsvc * ) sockaddr ;
if ( addr - > sas_family ! = AF_ATMSVC ) {
error = - EAFNOSUPPORT ;
goto out ;
}
2010-01-26 14:40:18 +03:00
clear_bit ( ATM_VF_BOUND , & vcc - > flags ) ;
2005-04-17 02:20:36 +04:00
/* failing rebind will kill old binding */
/* @@@ check memory (de)allocation on rebind */
2010-01-26 14:40:18 +03:00
if ( ! test_bit ( ATM_VF_HASQOS , & vcc - > flags ) ) {
2005-04-17 02:20:36 +04:00
error = - EBADFD ;
goto out ;
}
vcc - > local = * addr ;
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2010-01-26 14:40:18 +03:00
sigd_enq ( vcc , as_bind , NULL , NULL , & vcc - > local ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2010-01-26 14:40:18 +03:00
clear_bit ( ATM_VF_REGIS , & vcc - > flags ) ; /* doesn't count */
2005-04-17 02:20:36 +04:00
if ( ! sigd ) {
error = - EUNATCH ;
goto out ;
}
2007-02-09 17:24:29 +03:00
if ( ! sk - > sk_err )
2010-01-26 14:40:18 +03:00
set_bit ( ATM_VF_BOUND , & vcc - > flags ) ;
2005-04-17 02:20:36 +04:00
error = - sk - > sk_err ;
out :
release_sock ( sk ) ;
return error ;
}
2010-01-26 14:40:18 +03:00
static int svc_connect ( struct socket * sock , struct sockaddr * sockaddr ,
int sockaddr_len , int flags )
2005-04-17 02:20:36 +04:00
{
DEFINE_WAIT ( wait ) ;
struct sock * sk = sock - > sk ;
struct sockaddr_atmsvc * addr ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
int error ;
2010-01-26 14:40:18 +03:00
pr_debug ( " %p \n " , vcc ) ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
if ( sockaddr_len ! = sizeof ( struct sockaddr_atmsvc ) ) {
error = - EINVAL ;
goto out ;
}
switch ( sock - > state ) {
default :
error = - EINVAL ;
goto out ;
case SS_CONNECTED :
error = - EISCONN ;
goto out ;
case SS_CONNECTING :
if ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) ) {
error = - EALREADY ;
goto out ;
}
sock - > state = SS_UNCONNECTED ;
if ( sk - > sk_err ) {
error = - sk - > sk_err ;
goto out ;
}
break ;
case SS_UNCONNECTED :
addr = ( struct sockaddr_atmsvc * ) sockaddr ;
if ( addr - > sas_family ! = AF_ATMSVC ) {
error = - EAFNOSUPPORT ;
goto out ;
}
if ( ! test_bit ( ATM_VF_HASQOS , & vcc - > flags ) ) {
error = - EBADFD ;
goto out ;
}
if ( vcc - > qos . txtp . traffic_class = = ATM_ANYCLASS | |
vcc - > qos . rxtp . traffic_class = = ATM_ANYCLASS ) {
error = - EINVAL ;
goto out ;
}
if ( ! vcc - > qos . txtp . traffic_class & &
! vcc - > qos . rxtp . traffic_class ) {
error = - EINVAL ;
goto out ;
}
vcc - > remote = * addr ;
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2010-01-26 14:40:18 +03:00
sigd_enq ( vcc , as_connect , NULL , NULL , & vcc - > remote ) ;
2005-04-17 02:20:36 +04:00
if ( flags & O_NONBLOCK ) {
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
sock - > state = SS_CONNECTING ;
error = - EINPROGRESS ;
goto out ;
}
error = 0 ;
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
if ( ! signal_pending ( current ) ) {
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
continue ;
}
2007-08-29 02:22:09 +04:00
pr_debug ( " *ABORT* \n " ) ;
2005-04-17 02:20:36 +04:00
/*
* This is tricky :
* Kernel - - - close - - > Demon
* Kernel < - - close - - - Demon
2007-02-09 17:24:29 +03:00
* or
2005-04-17 02:20:36 +04:00
* Kernel - - - close - - > Demon
* Kernel < - - error - - - Demon
* or
* Kernel - - - close - - > Demon
* Kernel < - - okay - - - - Demon
* Kernel < - - close - - - Demon
*/
2010-01-26 14:40:18 +03:00
sigd_enq ( vcc , as_close , NULL , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
}
if ( ! sk - > sk_err )
2010-01-26 14:40:18 +03:00
while ( ! test_bit ( ATM_VF_RELEASED , & vcc - > flags ) & &
sigd ) {
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
schedule ( ) ;
}
2010-01-26 14:40:18 +03:00
clear_bit ( ATM_VF_REGIS , & vcc - > flags ) ;
clear_bit ( ATM_VF_RELEASED , & vcc - > flags ) ;
clear_bit ( ATM_VF_CLOSE , & vcc - > flags ) ;
2005-04-17 02:20:36 +04:00
/* we're gone now but may connect later */
error = - EINTR ;
break ;
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
if ( ! sigd ) {
error = - EUNATCH ;
goto out ;
}
if ( sk - > sk_err ) {
error = - sk - > sk_err ;
goto out ;
}
}
/*
* Not supported yet
*
* # ifndef CONFIG_SINGLE_SIGITF
*/
vcc - > qos . txtp . max_pcr = SELECT_TOP_PCR ( vcc - > qos . txtp ) ;
vcc - > qos . txtp . pcr = 0 ;
vcc - > qos . txtp . min_pcr = 0 ;
/*
* # endif
*/
2010-01-26 14:40:18 +03:00
error = vcc_connect ( sock , vcc - > itf , vcc - > vpi , vcc - > vci ) ;
if ( ! error )
2005-04-17 02:20:36 +04:00
sock - > state = SS_CONNECTED ;
else
2010-01-26 14:40:18 +03:00
( void ) svc_disconnect ( vcc ) ;
2005-04-17 02:20:36 +04:00
out :
release_sock ( sk ) ;
return error ;
}
2010-01-26 14:40:18 +03:00
static int svc_listen ( struct socket * sock , int backlog )
2005-04-17 02:20:36 +04:00
{
DEFINE_WAIT ( wait ) ;
struct sock * sk = sock - > sk ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
int error ;
2010-01-26 14:40:00 +03:00
pr_debug ( " %p \n " , vcc ) ;
2005-04-17 02:20:36 +04:00
lock_sock ( sk ) ;
/* let server handle listen on unbound sockets */
2010-01-26 14:40:18 +03:00
if ( test_bit ( ATM_VF_SESSION , & vcc - > flags ) ) {
2005-04-17 02:20:36 +04:00
error = - EINVAL ;
goto out ;
}
2008-12-05 01:58:13 +03:00
if ( test_bit ( ATM_VF_LISTEN , & vcc - > flags ) ) {
error = - EADDRINUSE ;
goto out ;
2010-01-26 14:40:18 +03:00
}
2005-04-17 02:20:36 +04:00
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2010-01-26 14:40:18 +03:00
sigd_enq ( vcc , as_listen , NULL , NULL , & vcc - > local ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( ! sigd ) {
error = - EUNATCH ;
goto out ;
}
2010-01-26 14:40:18 +03:00
set_bit ( ATM_VF_LISTEN , & vcc - > flags ) ;
2008-12-05 01:58:13 +03:00
vcc_insert_socket ( sk ) ;
2005-04-17 02:20:36 +04:00
sk - > sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT ;
error = - sk - > sk_err ;
out :
release_sock ( sk ) ;
return error ;
}
2010-01-26 14:40:18 +03:00
static int svc_accept ( struct socket * sock , struct socket * newsock , int flags )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
struct sk_buff * skb ;
struct atmsvc_msg * msg ;
struct atm_vcc * old_vcc = ATM_SD ( sock ) ;
struct atm_vcc * new_vcc ;
int error ;
lock_sock ( sk ) ;
2009-11-06 09:18:14 +03:00
error = svc_create ( sock_net ( sk ) , newsock , 0 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
new_vcc = ATM_SD ( newsock ) ;
2010-01-26 14:40:00 +03:00
pr_debug ( " %p -> %p \n " , old_vcc , new_vcc ) ;
2005-04-17 02:20:36 +04:00
while ( 1 ) {
DEFINE_WAIT ( wait ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
while ( ! ( skb = skb_dequeue ( & sk - > sk_receive_queue ) ) & &
sigd ) {
2010-01-26 14:40:18 +03:00
if ( test_bit ( ATM_VF_RELEASED , & old_vcc - > flags ) )
break ;
if ( test_bit ( ATM_VF_CLOSE , & old_vcc - > flags ) ) {
2005-04-17 02:20:36 +04:00
error = - sk - > sk_err ;
break ;
}
if ( flags & O_NONBLOCK ) {
error = - EAGAIN ;
break ;
}
release_sock ( sk ) ;
schedule ( ) ;
lock_sock ( sk ) ;
if ( signal_pending ( current ) ) {
error = - ERESTARTSYS ;
break ;
}
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( error )
goto out ;
if ( ! skb ) {
error = - EUNATCH ;
goto out ;
}
2010-01-26 14:40:18 +03:00
msg = ( struct atmsvc_msg * ) skb - > data ;
2005-04-17 02:20:36 +04:00
new_vcc - > qos = msg - > qos ;
2010-01-26 14:40:18 +03:00
set_bit ( ATM_VF_HASQOS , & new_vcc - > flags ) ;
2005-04-17 02:20:36 +04:00
new_vcc - > remote = msg - > svc ;
new_vcc - > local = msg - > local ;
new_vcc - > sap = msg - > sap ;
error = vcc_connect ( newsock , msg - > pvc . sap_addr . itf ,
2010-01-26 14:40:18 +03:00
msg - > pvc . sap_addr . vpi ,
msg - > pvc . sap_addr . vci ) ;
2005-04-17 02:20:36 +04:00
dev_kfree_skb ( skb ) ;
sk - > sk_ack_backlog - - ;
if ( error ) {
2010-01-26 14:40:18 +03:00
sigd_enq2 ( NULL , as_reject , old_vcc , NULL , NULL ,
& old_vcc - > qos , error ) ;
2005-04-17 02:20:36 +04:00
error = error = = - EAGAIN ? - EBUSY : error ;
goto out ;
}
/* wait should be short, so we ignore the non-blocking flag */
set_bit ( ATM_VF_WAITING , & new_vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk_atm ( new_vcc ) ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_UNINTERRUPTIBLE ) ;
sigd_enq ( new_vcc , as_accept , old_vcc , NULL , NULL ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & new_vcc - > flags ) & & sigd ) {
release_sock ( sk ) ;
schedule ( ) ;
lock_sock ( sk ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk_atm ( new_vcc ) ) , & wait ,
2010-01-26 14:40:18 +03:00
TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk_atm ( new_vcc ) ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( ! sigd ) {
error = - EUNATCH ;
goto out ;
}
if ( ! sk_atm ( new_vcc ) - > sk_err )
break ;
if ( sk_atm ( new_vcc ) - > sk_err ! = ERESTARTSYS ) {
error = - sk_atm ( new_vcc ) - > sk_err ;
goto out ;
}
}
newsock - > state = SS_CONNECTED ;
out :
release_sock ( sk ) ;
return error ;
}
2010-01-26 14:40:18 +03:00
static int svc_getname ( struct socket * sock , struct sockaddr * sockaddr ,
int * sockaddr_len , int peer )
2005-04-17 02:20:36 +04:00
{
struct sockaddr_atmsvc * addr ;
* sockaddr_len = sizeof ( struct sockaddr_atmsvc ) ;
addr = ( struct sockaddr_atmsvc * ) sockaddr ;
2010-01-26 14:40:18 +03:00
memcpy ( addr , peer ? & ATM_SD ( sock ) - > remote : & ATM_SD ( sock ) - > local ,
sizeof ( struct sockaddr_atmsvc ) ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2010-01-26 14:40:18 +03:00
int svc_change_qos ( struct atm_vcc * vcc , struct atm_qos * qos )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sk_atm ( vcc ) ;
DEFINE_WAIT ( wait ) ;
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2010-01-26 14:40:18 +03:00
sigd_enq2 ( vcc , as_modify , NULL , NULL , & vcc - > local , qos , 0 ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & &
! test_bit ( ATM_VF_RELEASED , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_UNINTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2010-01-26 14:40:18 +03:00
if ( ! sigd )
return - EUNATCH ;
2005-04-17 02:20:36 +04:00
return - sk - > sk_err ;
}
static int svc_setsockopt ( struct socket * sock , int level , int optname ,
2009-10-01 03:12:20 +04:00
char __user * optval , unsigned int optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
int value , error = 0 ;
lock_sock ( sk ) ;
switch ( optname ) {
2010-01-26 14:40:18 +03:00
case SO_ATMSAP :
if ( level ! = SOL_ATM | | optlen ! = sizeof ( struct atm_sap ) ) {
error = - EINVAL ;
goto out ;
}
if ( copy_from_user ( & vcc - > sap , optval , optlen ) ) {
error = - EFAULT ;
goto out ;
}
set_bit ( ATM_VF_HASSAP , & vcc - > flags ) ;
break ;
case SO_MULTIPOINT :
if ( level ! = SOL_ATM | | optlen ! = sizeof ( int ) ) {
error = - EINVAL ;
goto out ;
}
if ( get_user ( value , ( int __user * ) optval ) ) {
error = - EFAULT ;
goto out ;
}
if ( value = = 1 )
set_bit ( ATM_VF_SESSION , & vcc - > flags ) ;
else if ( value = = 0 )
clear_bit ( ATM_VF_SESSION , & vcc - > flags ) ;
else
error = - EINVAL ;
break ;
default :
error = vcc_setsockopt ( sock , level , optname , optval , optlen ) ;
2005-04-17 02:20:36 +04:00
}
out :
release_sock ( sk ) ;
return error ;
}
2010-01-26 14:40:18 +03:00
static int svc_getsockopt ( struct socket * sock , int level , int optname ,
char __user * optval , int __user * optlen )
2005-04-17 02:20:36 +04:00
{
struct sock * sk = sock - > sk ;
int error = 0 , len ;
lock_sock ( sk ) ;
if ( ! __SO_LEVEL_MATCH ( optname , level ) | | optname ! = SO_ATMSAP ) {
error = vcc_getsockopt ( sock , level , optname , optval , optlen ) ;
goto out ;
}
if ( get_user ( len , optlen ) ) {
error = - EFAULT ;
goto out ;
}
if ( len ! = sizeof ( struct atm_sap ) ) {
error = - EINVAL ;
goto out ;
}
if ( copy_to_user ( optval , & ATM_SD ( sock ) - > sap , sizeof ( struct atm_sap ) ) ) {
error = - EFAULT ;
goto out ;
}
out :
release_sock ( sk ) ;
return error ;
}
static int svc_addparty ( struct socket * sock , struct sockaddr * sockaddr ,
int sockaddr_len , int flags )
{
DEFINE_WAIT ( wait ) ;
struct sock * sk = sock - > sk ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
int error ;
lock_sock ( sk ) ;
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
sigd_enq ( vcc , as_addparty , NULL , NULL ,
2007-02-09 17:24:29 +03:00
( struct sockaddr_atmsvc * ) sockaddr ) ;
2005-04-17 02:20:36 +04:00
if ( flags & O_NONBLOCK ) {
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
error = - EINPROGRESS ;
goto out ;
}
2010-01-26 14:40:00 +03:00
pr_debug ( " added wait queue \n " ) ;
2005-04-17 02:20:36 +04:00
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
error = xchg ( & sk - > sk_err_soft , 0 ) ;
out :
release_sock ( sk ) ;
return error ;
}
static int svc_dropparty ( struct socket * sock , int ep_ref )
{
DEFINE_WAIT ( wait ) ;
struct sock * sk = sock - > sk ;
struct atm_vcc * vcc = ATM_SD ( sock ) ;
int error ;
lock_sock ( sk ) ;
set_bit ( ATM_VF_WAITING , & vcc - > flags ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
sigd_enq2 ( vcc , as_dropparty , NULL , NULL , NULL , NULL , ep_ref ) ;
while ( test_bit ( ATM_VF_WAITING , & vcc - > flags ) & & sigd ) {
schedule ( ) ;
2010-04-20 17:03:51 +04:00
prepare_to_wait ( sk_sleep ( sk ) , & wait , TASK_INTERRUPTIBLE ) ;
2005-04-17 02:20:36 +04:00
}
2010-04-20 17:03:51 +04:00
finish_wait ( sk_sleep ( sk ) , & wait ) ;
2005-04-17 02:20:36 +04:00
if ( ! sigd ) {
error = - EUNATCH ;
goto out ;
}
error = xchg ( & sk - > sk_err_soft , 0 ) ;
out :
release_sock ( sk ) ;
return error ;
}
static int svc_ioctl ( struct socket * sock , unsigned int cmd , unsigned long arg )
{
2007-02-09 17:24:29 +03:00
int error , ep_ref ;
struct sockaddr_atmsvc sa ;
2005-04-17 02:20:36 +04:00
struct atm_vcc * vcc = ATM_SD ( sock ) ;
2007-02-09 17:24:29 +03:00
2005-04-17 02:20:36 +04:00
switch ( cmd ) {
2010-01-26 14:40:18 +03:00
case ATM_ADDPARTY :
if ( ! test_bit ( ATM_VF_SESSION , & vcc - > flags ) )
return - EINVAL ;
if ( copy_from_user ( & sa , ( void __user * ) arg , sizeof ( sa ) ) )
return - EFAULT ;
error = svc_addparty ( sock , ( struct sockaddr * ) & sa , sizeof ( sa ) ,
0 ) ;
break ;
case ATM_DROPPARTY :
if ( ! test_bit ( ATM_VF_SESSION , & vcc - > flags ) )
return - EINVAL ;
if ( copy_from_user ( & ep_ref , ( void __user * ) arg , sizeof ( int ) ) )
return - EFAULT ;
error = svc_dropparty ( sock , ep_ref ) ;
break ;
default :
error = vcc_ioctl ( sock , cmd , arg ) ;
2005-04-17 02:20:36 +04:00
}
return error ;
}
atm: 32-bit ioctl compatibility
We lack compat ioctl support through most of the ATM code. This patch
deals with most of it, and I can now at least use BR2684 and PPPoATM
with 32-bit userspace.
I haven't added a .compat_ioctl method to struct atm_ioctl, because
AFAICT none of the current users need any conversion -- so we can just
call the ->ioctl() method in every case. I looked at br2684, clip, lec,
mpc, pppoatm and atmtcp.
In svc_compat_ioctl() the only mangling which is needed is to change
COMPAT_ATM_ADDPARTY to ATM_ADDPARTY. Although it's defined as
_IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
it doesn't actually _take_ a struct atm_iobuf as an argument -- it takes
a struct sockaddr_atmsvc, which _is_ the same between 32-bit and 64-bit
code, so doesn't need conversion.
Almost all of vcc_ioctl() would have been identical, so I converted that
into a core do_vcc_ioctl() function with an 'int compat' argument.
I've done the same with atm_dev_ioctl(), where there _are_ a few
differences, but still it's relatively contained and there would
otherwise have been a lot of duplication.
I haven't done any of the actual device-specific ioctls, although I've
added a compat_ioctl method to struct atmdev_ops.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-12-04 09:12:38 +03:00
# ifdef CONFIG_COMPAT
2010-01-26 14:40:18 +03:00
static int svc_compat_ioctl ( struct socket * sock , unsigned int cmd ,
unsigned long arg )
atm: 32-bit ioctl compatibility
We lack compat ioctl support through most of the ATM code. This patch
deals with most of it, and I can now at least use BR2684 and PPPoATM
with 32-bit userspace.
I haven't added a .compat_ioctl method to struct atm_ioctl, because
AFAICT none of the current users need any conversion -- so we can just
call the ->ioctl() method in every case. I looked at br2684, clip, lec,
mpc, pppoatm and atmtcp.
In svc_compat_ioctl() the only mangling which is needed is to change
COMPAT_ATM_ADDPARTY to ATM_ADDPARTY. Although it's defined as
_IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
it doesn't actually _take_ a struct atm_iobuf as an argument -- it takes
a struct sockaddr_atmsvc, which _is_ the same between 32-bit and 64-bit
code, so doesn't need conversion.
Almost all of vcc_ioctl() would have been identical, so I converted that
into a core do_vcc_ioctl() function with an 'int compat' argument.
I've done the same with atm_dev_ioctl(), where there _are_ a few
differences, but still it's relatively contained and there would
otherwise have been a lot of duplication.
I haven't done any of the actual device-specific ioctls, although I've
added a compat_ioctl method to struct atmdev_ops.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-12-04 09:12:38 +03:00
{
/* The definition of ATM_ADDPARTY uses the size of struct atm_iobuf.
But actually it takes a struct sockaddr_atmsvc , which doesn ' t need
compat handling . So all we have to do is fix up cmd . . . */
if ( cmd = = COMPAT_ATM_ADDPARTY )
cmd = ATM_ADDPARTY ;
if ( cmd = = ATM_ADDPARTY | | cmd = = ATM_DROPPARTY )
return svc_ioctl ( sock , cmd , arg ) ;
else
return vcc_compat_ioctl ( sock , cmd , arg ) ;
}
# endif /* CONFIG_COMPAT */
2005-12-22 23:49:22 +03:00
static const struct proto_ops svc_proto_ops = {
2005-04-17 02:20:36 +04:00
. family = PF_ATMSVC ,
. owner = THIS_MODULE ,
. release = svc_release ,
. bind = svc_bind ,
. connect = svc_connect ,
. socketpair = sock_no_socketpair ,
. accept = svc_accept ,
. getname = svc_getname ,
. poll = vcc_poll ,
. ioctl = svc_ioctl ,
atm: 32-bit ioctl compatibility
We lack compat ioctl support through most of the ATM code. This patch
deals with most of it, and I can now at least use BR2684 and PPPoATM
with 32-bit userspace.
I haven't added a .compat_ioctl method to struct atm_ioctl, because
AFAICT none of the current users need any conversion -- so we can just
call the ->ioctl() method in every case. I looked at br2684, clip, lec,
mpc, pppoatm and atmtcp.
In svc_compat_ioctl() the only mangling which is needed is to change
COMPAT_ATM_ADDPARTY to ATM_ADDPARTY. Although it's defined as
_IOW('a', ATMIOC_SPECIAL+4,struct atm_iobuf)
it doesn't actually _take_ a struct atm_iobuf as an argument -- it takes
a struct sockaddr_atmsvc, which _is_ the same between 32-bit and 64-bit
code, so doesn't need conversion.
Almost all of vcc_ioctl() would have been identical, so I converted that
into a core do_vcc_ioctl() function with an 'int compat' argument.
I've done the same with atm_dev_ioctl(), where there _are_ a few
differences, but still it's relatively contained and there would
otherwise have been a lot of duplication.
I haven't done any of the actual device-specific ioctls, although I've
added a compat_ioctl method to struct atmdev_ops.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2008-12-04 09:12:38 +03:00
# ifdef CONFIG_COMPAT
. compat_ioctl = svc_compat_ioctl ,
# endif
2005-04-17 02:20:36 +04:00
. listen = svc_listen ,
. shutdown = svc_shutdown ,
. setsockopt = svc_setsockopt ,
. getsockopt = svc_getsockopt ,
. sendmsg = vcc_sendmsg ,
. recvmsg = vcc_recvmsg ,
. mmap = sock_no_mmap ,
. sendpage = sock_no_sendpage ,
} ;
2009-11-06 09:18:14 +03:00
static int svc_create ( struct net * net , struct socket * sock , int protocol ,
int kern )
2005-04-17 02:20:36 +04:00
{
int error ;
2009-11-26 02:14:13 +03:00
if ( ! net_eq ( net , & init_net ) )
2007-10-09 10:24:22 +04:00
return - EAFNOSUPPORT ;
2005-04-17 02:20:36 +04:00
sock - > ops = & svc_proto_ops ;
2007-10-09 10:24:22 +04:00
error = vcc_create ( net , sock , protocol , AF_ATMSVC ) ;
2010-01-26 14:40:18 +03:00
if ( error )
return error ;
2005-04-17 02:20:36 +04:00
ATM_SD ( sock ) - > local . sas_family = AF_ATMSVC ;
ATM_SD ( sock ) - > remote . sas_family = AF_ATMSVC ;
return 0 ;
}
2009-10-05 09:58:39 +04:00
static const struct net_proto_family svc_family_ops = {
2005-04-17 02:20:36 +04:00
. family = PF_ATMSVC ,
. create = svc_create ,
. owner = THIS_MODULE ,
} ;
/*
* Initialize the ATM SVC protocol family
*/
int __init atmsvc_init ( void )
{
return sock_register ( & svc_family_ops ) ;
}
void atmsvc_exit ( void )
{
sock_unregister ( PF_ATMSVC ) ;
}