diff options
Diffstat (limited to 'net/caif')
-rw-r--r-- | net/caif/caif_dev.c | 25 | ||||
-rw-r--r-- | net/caif/caif_socket.c | 1795 | ||||
-rw-r--r-- | net/caif/cfcnfg.c | 195 | ||||
-rw-r--r-- | net/caif/cfctrl.c | 95 | ||||
-rw-r--r-- | net/caif/cfmuxl.c | 7 | ||||
-rw-r--r-- | net/caif/cfsrvl.c | 7 | ||||
-rw-r--r-- | net/caif/chnl_net.c | 130 |
7 files changed, 1059 insertions, 1195 deletions
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c index e84837e1bc86..024fd5bb2d39 100644 --- a/net/caif/caif_dev.c +++ b/net/caif/caif_dev.c @@ -247,10 +247,10 @@ static int caif_device_notify(struct notifier_block *me, unsigned long what, switch (caifdev->link_select) { case CAIF_LINK_HIGH_BANDW: - pref = CFPHYPREF_LOW_LAT; + pref = CFPHYPREF_HIGH_BW; break; case CAIF_LINK_LOW_LATENCY: - pref = CFPHYPREF_HIGH_BW; + pref = CFPHYPREF_LOW_LAT; break; default: pref = CFPHYPREF_HIGH_BW; @@ -330,23 +330,28 @@ int caif_connect_client(struct caif_connect_request *conn_req, struct cflayer *client_layer) { struct cfctrl_link_param param; - if (connect_req_to_link_param(get_caif_conf(), conn_req, ¶m) == 0) - /* Hook up the adaptation layer. */ - return cfcnfg_add_adaptation_layer(get_caif_conf(), + int ret; + ret = connect_req_to_link_param(get_caif_conf(), conn_req, ¶m); + if (ret) + return ret; + /* Hook up the adaptation layer. */ + return cfcnfg_add_adaptation_layer(get_caif_conf(), ¶m, client_layer); - - return -EINVAL; - - caif_assert(0); } EXPORT_SYMBOL(caif_connect_client); int caif_disconnect_client(struct cflayer *adap_layer) { - return cfcnfg_del_adapt_layer(get_caif_conf(), adap_layer); + return cfcnfg_disconn_adapt_layer(get_caif_conf(), adap_layer); } EXPORT_SYMBOL(caif_disconnect_client); +void caif_release_client(struct cflayer *adap_layer) +{ + cfcnfg_release_adap_layer(adap_layer); +} +EXPORT_SYMBOL(caif_release_client); + /* Per-namespace Caif devices handling */ static int caif_init_net(struct net *net) { diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c index cdf62b9fefac..c3a70c5c893a 100644 --- a/net/caif/caif_socket.c +++ b/net/caif/caif_socket.c @@ -1,7 +1,6 @@ /* * Copyright (C) ST-Ericsson AB 2010 * Author: Sjur Brendeland sjur.brandeland@stericsson.com - * Per Sigmond per.sigmond@stericsson.com * License terms: GNU General Public License (GPL) version 2 */ @@ -16,91 +15,52 @@ #include <linux/poll.h> #include <linux/tcp.h> #include <linux/uaccess.h> -#include <asm/atomic.h> - +#include <linux/mutex.h> +#include <linux/debugfs.h> #include <linux/caif/caif_socket.h> +#include <asm/atomic.h> +#include <net/sock.h> +#include <net/tcp_states.h> #include <net/caif/caif_layer.h> #include <net/caif/caif_dev.h> #include <net/caif/cfpkt.h> MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(AF_CAIF); + +#define CAIF_DEF_SNDBUF (CAIF_MAX_PAYLOAD_SIZE*10) +#define CAIF_DEF_RCVBUF (CAIF_MAX_PAYLOAD_SIZE*100) + +/* + * CAIF state is re-using the TCP socket states. + * caif_states stored in sk_state reflect the state as reported by + * the CAIF stack, while sk_socket->state is the state of the socket. + */ +enum caif_states { + CAIF_CONNECTED = TCP_ESTABLISHED, + CAIF_CONNECTING = TCP_SYN_SENT, + CAIF_DISCONNECTED = TCP_CLOSE +}; + +#define TX_FLOW_ON_BIT 1 +#define RX_FLOW_ON_BIT 2 -#define CHNL_SKT_READ_QUEUE_HIGH 200 -#define CHNL_SKT_READ_QUEUE_LOW 100 - -static int caif_sockbuf_size = 40000; -static atomic_t caif_nr_socks = ATOMIC_INIT(0); - -#define CONN_STATE_OPEN_BIT 1 -#define CONN_STATE_PENDING_BIT 2 -#define CONN_STATE_PEND_DESTROY_BIT 3 -#define CONN_REMOTE_SHUTDOWN_BIT 4 - -#define TX_FLOW_ON_BIT 1 -#define RX_FLOW_ON_BIT 2 - -#define STATE_IS_OPEN(cf_sk) test_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_REMOTE_SHUTDOWN(cf_sk) test_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING(cf_sk) test_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define STATE_IS_PENDING_DESTROY(cf_sk) test_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_STATE_PENDING_DESTROY(cf_sk) set_bit(CONN_STATE_PEND_DESTROY_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_OPEN(cf_sk) set_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_STATE_CLOSED(cf_sk) clear_bit(CONN_STATE_OPEN_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_ON(cf_sk) set_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_PENDING_OFF(cf_sk) clear_bit(CONN_STATE_PENDING_BIT,\ - (void *) &(cf_sk)->conn_state) -#define SET_REMOTE_SHUTDOWN(cf_sk) set_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(cf_sk)->conn_state) - -#define SET_REMOTE_SHUTDOWN_OFF(dev) clear_bit(CONN_REMOTE_SHUTDOWN_BIT,\ - (void *) &(dev)->conn_state) -#define RX_FLOW_IS_ON(cf_sk) test_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define TX_FLOW_IS_ON(cf_sk) test_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SET_RX_FLOW_OFF(cf_sk) clear_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_RX_FLOW_ON(cf_sk) set_bit(RX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_OFF(cf_sk) clear_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) -#define SET_TX_FLOW_ON(cf_sk) set_bit(TX_FLOW_ON_BIT,\ - (void *) &(cf_sk)->flow_state) - -#define SKT_READ_FLAG 0x01 -#define SKT_WRITE_FLAG 0x02 static struct dentry *debugfsdir; -#include <linux/debugfs.h> #ifdef CONFIG_DEBUG_FS struct debug_fs_counter { - atomic_t num_open; - atomic_t num_close; - atomic_t num_init; - atomic_t num_init_resp; - atomic_t num_init_fail_resp; - atomic_t num_deinit; - atomic_t num_deinit_resp; + atomic_t caif_nr_socks; + atomic_t num_connect_req; + atomic_t num_connect_resp; + atomic_t num_connect_fail_resp; + atomic_t num_disconnect; atomic_t num_remote_shutdown_ind; atomic_t num_tx_flow_off_ind; atomic_t num_tx_flow_on_ind; atomic_t num_rx_flow_off; atomic_t num_rx_flow_on; - atomic_t skb_in_use; - atomic_t skb_alloc; - atomic_t skb_free; }; -static struct debug_fs_counter cnt; +struct debug_fs_counter cnt; #define dbfs_atomic_inc(v) atomic_inc(v) #define dbfs_atomic_dec(v) atomic_dec(v) #else @@ -108,624 +68,666 @@ static struct debug_fs_counter cnt; #define dbfs_atomic_dec(v) #endif -/* The AF_CAIF socket */ struct caifsock { - /* NOTE: sk has to be the first member */ - struct sock sk; + struct sock sk; /* must be first member */ struct cflayer layer; - char name[CAIF_LAYER_NAME_SZ]; - u32 conn_state; + char name[CAIF_LAYER_NAME_SZ]; /* Used for debugging */ u32 flow_state; - struct cfpktq *pktq; - int file_mode; struct caif_connect_request conn_req; - int read_queue_len; - /* protect updates of read_queue_len */ - spinlock_t read_queue_len_lock; + struct mutex readlock; struct dentry *debugfs_socket_dir; }; -static void drain_queue(struct caifsock *cf_sk); +static int rx_flow_is_on(struct caifsock *cf_sk) +{ + return test_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} + +static int tx_flow_is_on(struct caifsock *cf_sk) +{ + return test_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} -/* Packet Receive Callback function called from CAIF Stack */ -static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) +static void set_rx_flow_off(struct caifsock *cf_sk) { - struct caifsock *cf_sk; - int read_queue_high; - cf_sk = container_of(layr, struct caifsock, layer); + clear_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - if (!STATE_IS_OPEN(cf_sk)) { - /*FIXME: This should be allowed finally!*/ - pr_debug("CAIF: %s(): called after close request\n", __func__); - cfpkt_destroy(pkt); - return 0; - } - /* NOTE: This function may be called in Tasklet context! */ +static void set_rx_flow_on(struct caifsock *cf_sk) +{ + set_bit(RX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - /* The queue has its own lock */ - cfpkt_queue(cf_sk->pktq, pkt, 0); +static void set_tx_flow_off(struct caifsock *cf_sk) +{ + clear_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len++; +static void set_tx_flow_on(struct caifsock *cf_sk) +{ + set_bit(TX_FLOW_ON_BIT, + (void *) &cf_sk->flow_state); +} - read_queue_high = (cf_sk->read_queue_len > CHNL_SKT_READ_QUEUE_HIGH); - spin_unlock(&cf_sk->read_queue_len_lock); +static void caif_read_lock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_lock(&cf_sk->readlock); +} - if (RX_FLOW_IS_ON(cf_sk) && read_queue_high) { - dbfs_atomic_inc(&cnt.num_rx_flow_off); - SET_RX_FLOW_OFF(cf_sk); +static void caif_read_unlock(struct sock *sk) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + mutex_unlock(&cf_sk->readlock); +} - /* Send flow off (NOTE: must not sleep) */ - pr_debug("CAIF: %s():" - " sending flow OFF (queue len = %d)\n", - __func__, - cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); +int sk_rcvbuf_lowwater(struct caifsock *cf_sk) +{ + /* A quarter of full buffer is used a low water mark */ + return cf_sk->sk.sk_rcvbuf / 4; +} - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_OFF_REQ); - } +void caif_flow_ctrl(struct sock *sk, int mode) +{ + struct caifsock *cf_sk; + cf_sk = container_of(sk, struct caifsock, sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode); +} - /* Signal reader that data is available. */ +/* + * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are + * not dropped, but CAIF is sending flow off instead. + */ +int caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) +{ + int err; + int skb_len; + unsigned long flags; + struct sk_buff_head *list = &sk->sk_receive_queue; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >= + (unsigned)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) { + trace_printk("CAIF: %s():" + " sending flow OFF (queue len = %d %d)\n", + __func__, + atomic_read(&cf_sk->sk.sk_rmem_alloc), + sk_rcvbuf_lowwater(cf_sk)); + set_rx_flow_off(cf_sk); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); + } + err = sk_filter(sk, skb); + if (err) + return err; + if (!sk_rmem_schedule(sk, skb->truesize) && rx_flow_is_on(cf_sk)) { + set_rx_flow_off(cf_sk); + trace_printk("CAIF: %s():" + " sending flow OFF due to rmem_schedule\n", + __func__); + if (cf_sk->layer.dn) + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_OFF_REQ); + } + skb->dev = NULL; + skb_set_owner_r(skb, sk); + /* Cache the SKB length before we tack it onto the receive + * queue. Once it is added it no longer belongs to us and + * may be freed by other threads of control pulling packets + * from the queue. + */ + skb_len = skb->len; + spin_lock_irqsave(&list->lock, flags); + if (!sock_flag(sk, SOCK_DEAD)) + __skb_queue_tail(list, skb); + spin_unlock_irqrestore(&list->lock, flags); + + if (!sock_flag(sk, SOCK_DEAD)) + sk->sk_data_ready(sk, skb_len); + else + kfree_skb(skb); return 0; } -/* Packet Flow Control Callback function called from CAIF */ -static void caif_sktflowctrl_cb(struct cflayer *layr, - enum caif_ctrlcmd flow, - int phyid) +/* Packet Receive Callback function called from CAIF Stack */ +static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt) { struct caifsock *cf_sk; - - /* NOTE: This function may be called in Tasklet context! */ - pr_debug("CAIF: %s(): flowctrl func called: %s.\n", - __func__, - flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : - flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" : - flow == CAIF_CTRLCMD_INIT_RSP ? "INIT_RSP" : - flow == CAIF_CTRLCMD_DEINIT_RSP ? "DEINIT_RSP" : - flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "INIT_FAIL_RSP" : - flow == - CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" : - "UKNOWN CTRL COMMAND"); - - if (layr == NULL) - return; + struct sk_buff *skb; cf_sk = container_of(layr, struct caifsock, layer); + skb = cfpkt_tonative(pkt); + + if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) { + cfpkt_destroy(pkt); + return 0; + } + caif_queue_rcv_skb(&cf_sk->sk, skb); + return 0; +} +/* Packet Control Callback function called from CAIF */ +static void caif_ctrl_cb(struct cflayer *layr, + enum caif_ctrlcmd flow, + int phyid) +{ + struct caifsock *cf_sk = container_of(layr, struct caifsock, layer); switch (flow) { case CAIF_CTRLCMD_FLOW_ON_IND: + /* OK from modem to start sending again */ dbfs_atomic_inc(&cnt.num_tx_flow_on_ind); - /* Signal reader that data is available. */ - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_FLOW_OFF_IND: + /* Modem asks us to shut up */ dbfs_atomic_inc(&cnt.num_tx_flow_off_ind); - SET_TX_FLOW_OFF(cf_sk); + set_tx_flow_off(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_INIT_RSP: - dbfs_atomic_inc(&cnt.num_init_resp); - /* Signal reader that data is available. */ - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_ON(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + /* We're now connected */ + dbfs_atomic_inc(&cnt.num_connect_resp); + cf_sk->sk.sk_state = CAIF_CONNECTED; + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_DEINIT_RSP: - dbfs_atomic_inc(&cnt.num_deinit_resp); - caif_assert(!STATE_IS_OPEN(cf_sk)); - SET_PENDING_OFF(cf_sk); - if (!STATE_IS_PENDING_DESTROY(cf_sk)) { - if (cf_sk->sk.sk_sleep != NULL) - wake_up_interruptible(cf_sk->sk.sk_sleep); - } - dbfs_atomic_inc(&cnt.num_deinit); - sock_put(&cf_sk->sk); + /* We're now disconnected */ + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_state_change(&cf_sk->sk); + cfcnfg_release_adap_layer(&cf_sk->layer); break; case CAIF_CTRLCMD_INIT_FAIL_RSP: - dbfs_atomic_inc(&cnt.num_init_fail_resp); - caif_assert(STATE_IS_OPEN(cf_sk)); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - wake_up_interruptible(cf_sk->sk.sk_sleep); + /* Connect request failed */ + dbfs_atomic_inc(&cnt.num_connect_fail_resp); + cf_sk->sk.sk_err = ECONNREFUSED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + /* + * Socket "standards" seems to require POLLOUT to + * be set at connect failure. + */ + set_tx_flow_on(cf_sk); + cf_sk->sk.sk_state_change(&cf_sk->sk); break; case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: + /* Modem has closed this connection, or device is down. */ dbfs_atomic_inc(&cnt.num_remote_shutdown_ind); - SET_REMOTE_SHUTDOWN(cf_sk); - /* Use sk_shutdown to indicate remote shutdown indication */ - cf_sk->sk.sk_shutdown |= RCV_SHUTDOWN; - cf_sk->file_mode = 0; - wake_up_interruptible(cf_sk->sk.sk_sleep); + cf_sk->sk.sk_shutdown = SHUTDOWN_MASK; + cf_sk->sk.sk_err = ECONNRESET; + set_rx_flow_on(cf_sk); + cf_sk->sk.sk_error_report(&cf_sk->sk); break; default: pr_debug("CAIF: %s(): Unexpected flow command %d\n", - __func__, flow); + __func__, flow); } } -static void skb_destructor(struct sk_buff *skb) +static void caif_check_flow_release(struct sock *sk) { - dbfs_atomic_inc(&cnt.skb_free); - dbfs_atomic_dec(&cnt.skb_in_use); -} + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + if (cf_sk->layer.dn == NULL || cf_sk->layer.dn->modemcmd == NULL) + return; + if (rx_flow_is_on(cf_sk)) + return; -static int caif_recvmsg(struct kiocb *iocb, struct socket *sock, + if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) { + dbfs_atomic_inc(&cnt.num_rx_flow_on); + set_rx_flow_on(cf_sk); + cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, + CAIF_MODEMCMD_FLOW_ON_REQ); + } +} +/* + * Copied from sock.c:sock_queue_rcv_skb(), and added check that user buffer + * has sufficient size. + */ + +static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t buf_len, int flags) { struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - struct cfpkt *pkt = NULL; - size_t len; - int result; struct sk_buff *skb; - ssize_t ret = -EIO; - int read_queue_low; - - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto read_error; - } - - /* Don't do multiple iovec entries yet */ - if (m->msg_iovlen != 1) - return -EOPNOTSUPP; + int ret = 0; + int len; if (unlikely(!buf_len)) return -EINVAL; - lock_sock(&(cf_sk->sk)); - - caif_assert(cf_sk->pktq); - - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing. */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; - } else { - pr_debug("CAIF: %s(): socket is closing..\n", __func__); - ret = -EBADF; - } + skb = skb_recv_datagram(sk, flags, 0 , &ret); + if (!skb) goto read_error; - } - /* Socket is open or opening. */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); - - if (flags & MSG_DONTWAIT) { - /* We can't block. */ - pr_debug("CAIF: %s():state pending and MSG_DONTWAIT\n", - __func__); - ret = -EAGAIN; - goto read_error; - } + len = skb->len; + + if (skb && skb->len > buf_len && !(flags & MSG_PEEK)) { + len = buf_len; /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. + * Push skb back on receive queue if buffer too small. + * This has a built-in race where multi-threaded receive + * may get packet in wrong order, but multiple read does + * not really guarantee ordered delivery anyway. + * Let's optimize for speed without taking locks. */ - release_sock(&cf_sk->sk); - - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - lock_sock(&(cf_sk->sk)); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto read_error; - } + skb_queue_head(&sk->sk_receive_queue, skb); + ret = -EMSGSIZE; + goto read_error; } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) || - !STATE_IS_OPEN(cf_sk) || - STATE_IS_PENDING(cf_sk)) { - - pr_debug("CAIF: %s(): socket closed\n", - __func__); - ret = -ESHUTDOWN; + ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); + if (ret) goto read_error; - } - /* - * Block if we don't have any received buffers. - * The queue has its own lock. - */ - while ((pkt = cfpkt_qpeek(cf_sk->pktq)) == NULL) { + skb_free_datagram(sk, skb); - if (flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto read_error; - } - trace_printk("CAIF: %s() wait_event\n", __func__); + caif_check_flow_release(sk); - /* Let writers in. */ - release_sock(&cf_sk->sk); + return len; - /* Block reader until data arrives or socket is closed. */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - cfpkt_qpeek(cf_sk->pktq) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - || !STATE_IS_OPEN(cf_sk)) == - -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by " - "a signal, signal_pending(current) = %d\n", - __func__, - signal_pending(current)); - return -ERESTARTSYS; - } +read_error: + return ret; +} - trace_printk("CAIF: %s() awake\n", __func__); - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto read_error_no_unlock; - } - /* I want to be alone on cf_sk (except status and queue). */ - lock_sock(&(cf_sk->sk)); +/* Copied from unix_stream_wait_data, identical except for lock call. */ +static long caif_stream_data_wait(struct sock *sk, long timeo) +{ + DEFINE_WAIT(wait); + lock_sock(sk); + + for (;;) { + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + if (!skb_queue_empty(&sk->sk_receive_queue) || + sk->sk_err || + sk->sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN) || + signal_pending(current) || + !timeo) + break; - if (!STATE_IS_OPEN(cf_sk)) { - /* Someone closed the link, report error. */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto read_error; - } + set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); } - /* The queue has its own lock. */ - len = cfpkt_getlen(pkt); - - /* Check max length that can be copied. */ - if (len <= buf_len) - pkt = cfpkt_dequeue(cf_sk->pktq); - else { - pr_debug("CAIF: %s(): user buffer too small (%ld,%ld)\n", - __func__, (long) len, (long) buf_len); - if (sock->type == SOCK_SEQPACKET) { - ret = -EMSGSIZE; - goto read_error; - } - len = buf_len; - } + finish_wait(sk_sleep(sk), &wait); + release_sock(sk); + return timeo; +} - spin_lock(&cf_sk->read_queue_len_lock); - cf_sk->read_queue_len--; - read_queue_low = (cf_sk->read_queue_len < CHNL_SKT_READ_QUEUE_LOW); - spin_unlock(&cf_sk->read_queue_len_lock); +/* + * Copied from unix_stream_recvmsg, but removed credit checks, + * changed locking calls, changed address handling. + */ +static int caif_stream_recvmsg(struct kiocb *iocb, struct socket *sock, + struct msghdr *msg, size_t size, + int flags) +{ + struct sock *sk = sock->sk; + int copied = 0; + int target; + int err = 0; + long timeo; - if (!RX_FLOW_IS_ON(cf_sk) && read_queue_low) { - dbfs_atomic_inc(&cnt.num_rx_flow_on); - SET_RX_FLOW_ON(cf_sk); + err = -EOPNOTSUPP; + if (flags&MSG_OOB) + goto out; - /* Send flow on. */ - pr_debug("CAIF: %s(): sending flow ON (queue len = %d)\n", - __func__, cf_sk->read_queue_len); - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->ctrlcmd); - (void) cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, - CAIF_MODEMCMD_FLOW_ON_REQ); + msg->msg_namelen = 0; - caif_assert(cf_sk->read_queue_len >= 0); - } + /* + * Lock the socket to prevent queue disordering + * while sleeps in memcpy_tomsg + */ + err = -EAGAIN; + if (sk->sk_state == CAIF_CONNECTING) + goto out; - skb = cfpkt_tonative(pkt); - result = skb_copy_datagram_iovec(skb, 0, m->msg_iov, len); - skb_pull(skb, len); + caif_read_lock(sk); + target = sock_rcvlowat(sk, flags&MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT); - if (result) { - pr_debug("CAIF: %s(): copy to_iovec failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EFAULT; - goto read_error; - } + do { + int chunk; + struct sk_buff *skb; - /* Free packet and remove from queue */ - if (skb->len == 0) - skb_free_datagram(sk, skb); + lock_sock(sk); + skb = skb_dequeue(&sk->sk_receive_queue); + caif_check_flow_release(sk); - /* Let the others in. */ - release_sock(&cf_sk->sk); - return len; + if (skb == NULL) { + if (copied >= target) + goto unlock; + /* + * POSIX 1003.1g mandates this order. + */ + err = sock_error(sk); + if (err) + goto unlock; + err = -ECONNRESET; + if (sk->sk_shutdown & RCV_SHUTDOWN) + goto unlock; -read_error: - release_sock(&cf_sk->sk); -read_error_no_unlock: - return ret; -} + err = -EPIPE; + if (sk->sk_state != CAIF_CONNECTED) + goto unlock; + if (sock_flag(sk, SOCK_DEAD)) + goto unlock; -/* Send a signal as a consequence of sendmsg, sendto or caif_sendmsg. */ -static int caif_sendmsg(struct kiocb *kiocb, struct socket *sock, - struct msghdr *msg, size_t len) -{ + release_sock(sk); - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - size_t payload_size = msg->msg_iov->iov_len; - struct cfpkt *pkt = NULL; - struct caif_payload_info info; - unsigned char *txbuf; - ssize_t ret = -EIO; - int result; - struct sk_buff *skb; - caif_assert(msg->msg_iovlen == 1); + err = -EAGAIN; + if (!timeo) + break; - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): private_data not set!\n", - __func__); - ret = -EBADFD; - goto write_error_no_unlock; - } + caif_read_unlock(sk); - if (unlikely(msg->msg_iov->iov_base == NULL)) { - pr_warning("CAIF: %s(): Buffer is NULL.\n", __func__); - ret = -EINVAL; - goto write_error_no_unlock; - } + timeo = caif_stream_data_wait(sk, timeo); - if (payload_size > CAIF_MAX_PAYLOAD_SIZE) { - pr_debug("CAIF: %s(): buffer too long\n", __func__); - if (sock->type == SOCK_SEQPACKET) { - ret = -EINVAL; - goto write_error_no_unlock; + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + goto out; + } + caif_read_lock(sk); + continue; +unlock: + release_sock(sk); + break; } - payload_size = CAIF_MAX_PAYLOAD_SIZE; - } + release_sock(sk); + chunk = min_t(unsigned int, skb->len, size); + if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + skb_queue_head(&sk->sk_receive_queue, skb); + if (copied == 0) + copied = -EFAULT; + break; + } + copied += chunk; + size -= chunk; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + /* Mark read part of skb as used */ + if (!(flags & MSG_PEEK)) { + skb_pull(skb, chunk); - caif_assert(cf_sk->pktq); + /* put the skb back if we didn't use it up. */ + if (skb->len) { + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + kfree_skb(skb); - if (!STATE_IS_OPEN(cf_sk)) { - /* Socket is closed or closing */ - if (!STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is closed (by remote)\n", - __func__); - ret = -EPIPE; } else { - pr_debug("CAIF: %s(): socket is closing...\n", - __func__); - ret = -EBADF; - } - goto write_error; - } - - /* Socket is open or opening */ - if (STATE_IS_PENDING(cf_sk)) { - pr_debug("CAIF: %s(): socket is opening...\n", __func__); - - if (msg->msg_flags & MSG_DONTWAIT) { - /* We can't block */ - trace_printk("CAIF: %s():state pending:" - "state=MSG_DONTWAIT\n", __func__); - ret = -EAGAIN; - goto write_error; + /* + * It is questionable, see note in unix_dgram_recvmsg. + */ + /* put message back and return */ + skb_queue_head(&sk->sk_receive_queue, skb); + break; } - /* Let readers in */ - release_sock(&cf_sk->sk); - - /* - * Blocking mode; state is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + } while (size); + caif_read_unlock(sk); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto write_error; - } - } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk) || - !STATE_IS_OPEN(cf_sk) || - STATE_IS_PENDING(cf_sk)) { +out: + return copied ? : err; +} - pr_debug("CAIF: %s(): socket closed\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; +/* + * Copied from sock.c:sock_wait_for_wmem, but change to wait for + * CAIF flow-on and sock_writable. + */ +static long caif_wait_for_flow_on(struct caifsock *cf_sk, + int wait_writeable, long timeo, int *err) +{ + struct sock *sk = &cf_sk->sk; + DEFINE_WAIT(wait); + for (;;) { + *err = 0; + if (tx_flow_is_on(cf_sk) && + (!wait_writeable || sock_writeable(&cf_sk->sk))) + break; + *err = -ETIMEDOUT; + if (!timeo) + break; + *err = -ERESTARTSYS; + if (signal_pending(current)) + break; + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + *err = -ECONNRESET; + if (sk->sk_shutdown & SHUTDOWN_MASK) + break; + *err = -sk->sk_err; + if (sk->sk_err) + break; + *err = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED) + break; + timeo = schedule_timeout(timeo); } + finish_wait(sk_sleep(sk), &wait); + return timeo; +} - if (!TX_FLOW_IS_ON(cf_sk)) { +/* + * Transmit a SKB. The device may temporarily request re-transmission + * by returning EAGAIN. + */ +static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk, + int noblock, long timeo) +{ + struct cfpkt *pkt; + int ret, loopcnt = 0; - /* Flow is off. Check non-block flag */ - if (msg->msg_flags & MSG_DONTWAIT) { - trace_printk("CAIF: %s(): MSG_DONTWAIT and tx flow off", - __func__); - ret = -EAGAIN; - goto write_error; - } + pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb); + memset(cfpkt_info(pkt), 0, sizeof(struct caif_payload_info)); + do { - /* release lock before waiting */ - release_sock(&cf_sk->sk); + ret = -ETIMEDOUT; - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s():" - " wait_event_interruptible woken by a signal", - __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; + /* Slight paranoia, probably not needed. */ + if (unlikely(loopcnt++ > 1000)) { + pr_warning("CAIF: %s(): transmit retries failed," + " error = %d\n", __func__, ret); + break; } - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - if (!STATE_IS_OPEN(cf_sk)) { - /* someone closed the link, report error */ - pr_debug("CAIF: %s(): remote end shutdown!\n", - __func__); - ret = -EPIPE; - goto write_error; + if (cf_sk->layer.dn != NULL) + ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); + if (likely(ret >= 0)) + break; + /* if transmit return -EAGAIN, then retry */ + if (noblock && ret == -EAGAIN) + break; + timeo = caif_wait_for_flow_on(cf_sk, 0, timeo, &ret); + if (signal_pending(current)) { + ret = sock_intr_errno(timeo); + break; } - - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - pr_debug("CAIF: %s(): " - "received remote_shutdown indication\n", - __func__); - ret = -ESHUTDOWN; - goto write_error; + if (ret) + break; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(&cf_sk->sk, SOCK_DEAD) || + (cf_sk->sk.sk_shutdown & RCV_SHUTDOWN)) { + ret = -EPIPE; + cf_sk->sk.sk_err = EPIPE; + break; } - } + } while (ret == -EAGAIN); + return ret; +} - pkt = cfpkt_create(payload_size); - skb = (struct sk_buff *)pkt; - skb->destructor = skb_destructor; - skb->sk = sk; - dbfs_atomic_inc(&cnt.skb_alloc); - dbfs_atomic_inc(&cnt.skb_in_use); - if (cfpkt_raw_append(pkt, (void **) &txbuf, payload_size) < 0) { - pr_debug("CAIF: %s(): cfpkt_raw_append failed\n", __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; - } +/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */ +static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int buffer_size; + int ret = 0; + struct sk_buff *skb = NULL; + int noblock; + long timeo; + caif_assert(cf_sk); + ret = sock_error(sk); + if (ret) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_flags&MSG_OOB) + goto err; + + ret = -EOPNOTSUPP; + if (msg->msg_namelen) + goto err; + + ret = -EINVAL; + if (unlikely(msg->msg_iov->iov_base == NULL)) + goto err; + noblock = msg->msg_flags & MSG_DONTWAIT; + + buffer_size = len + CAIF_NEEDED_HEADROOM + CAIF_NEEDED_TAILROOM; + + ret = -EMSGSIZE; + if (buffer_size > CAIF_MAX_PAYLOAD_SIZE) + goto err; + + timeo = sock_sndtimeo(sk, noblock); + timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk), + 1, timeo, &ret); + + ret = -EPIPE; + if (cf_sk->sk.sk_state != CAIF_CONNECTED || + sock_flag(sk, SOCK_DEAD) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + goto err; + + ret = -ENOMEM; + skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret); + if (!skb) + goto err; + skb_reserve(skb, CAIF_NEEDED_HEADROOM); + + ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + + if (ret) + goto err; + ret = transmit_skb(skb, cf_sk, noblock, timeo); + if (ret < 0) + goto err; + return len; +err: + kfree_skb(skb); + return ret; +} - /* Copy data into buffer. */ - if (copy_from_user(txbuf, msg->msg_iov->iov_base, payload_size)) { - pr_debug("CAIF: %s(): copy_from_user returned non zero.\n", - __func__); - cfpkt_destroy(pkt); - ret = -EINVAL; - goto write_error; - } - memset(&info, 0, sizeof(info)); +/* + * Copied from unix_stream_sendmsg and adapted to CAIF: + * Changed removed permission handling and added waiting for flow on + * and other minor adaptations. + */ +static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, + struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int err, size; + struct sk_buff *skb; + int sent = 0; + long timeo; - /* Send the packet down the stack. */ - caif_assert(cf_sk->layer.dn); - caif_assert(cf_sk->layer.dn->transmit); + err = -EOPNOTSUPP; - do { - ret = cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt); + if (unlikely(msg->msg_flags&MSG_OOB)) + goto out_err; - if (likely((ret >= 0) || (ret != -EAGAIN))) - break; + if (unlikely(msg->msg_namelen)) + goto out_err; - /* EAGAIN - retry */ - if (msg->msg_flags & MSG_DONTWAIT) { - pr_debug("CAIF: %s(): NONBLOCK and transmit failed," - " error = %ld\n", __func__, (long) ret); - ret = -EAGAIN; - goto write_error; - } + timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT); + timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err); - /* Let readers in */ - release_sock(&cf_sk->sk); + if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN)) + goto pipe_err; - /* Wait until flow is on or socket is closed */ - if (wait_event_interruptible(*cf_sk->sk.sk_sleep, - TX_FLOW_IS_ON(cf_sk) - || !STATE_IS_OPEN(cf_sk) - || STATE_IS_REMOTE_SHUTDOWN(cf_sk) - ) == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - " woken by a signal", __func__); - ret = -ERESTARTSYS; - goto write_error_no_unlock; - } + while (sent < len) { - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); + size = len-sent; - } while (ret == -EAGAIN); + if (size > CAIF_MAX_PAYLOAD_SIZE) + size = CAIF_MAX_PAYLOAD_SIZE; - if (ret < 0) { - cfpkt_destroy(pkt); - pr_debug("CAIF: %s(): transmit failed, error = %ld\n", - __func__, (long) ret); + /* If size is more than half of sndbuf, chop up message */ + if (size > ((sk->sk_sndbuf >> 1) - 64)) + size = (sk->sk_sndbuf >> 1) - 64; - goto write_error; - } + if (size > SKB_MAX_ALLOC) + size = SKB_MAX_ALLOC; - release_sock(&cf_sk->sk); - return payload_size; + skb = sock_alloc_send_skb(sk, + size + CAIF_NEEDED_HEADROOM + + CAIF_NEEDED_TAILROOM, + msg->msg_flags&MSG_DONTWAIT, + &err); + if (skb == NULL) + goto out_err; -write_error: - release_sock(&cf_sk->sk); -write_error_no_unlock: - return ret; -} + skb_reserve(skb, CAIF_NEEDED_HEADROOM); + /* + * If you pass two values to the sock_alloc_send_skb + * it tries to grab the large buffer with GFP_NOFS + * (which can fail easily), and if it fails grab the + * fallback size buffer which is under a page and will + * succeed. [Alan] + */ + size = min_t(int, size, skb_tailroom(skb)); -static unsigned int caif_poll(struct file *file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - u32 mask = 0; - poll_wait(file, sk->sk_sleep, wait); - lock_sock(&(cf_sk->sk)); - if (!STATE_IS_OPEN(cf_sk)) { - if (!STATE_IS_PENDING(cf_sk)) - mask |= POLLHUP; - } else { - if (cfpkt_qpeek(cf_sk->pktq) != NULL) - mask |= (POLLIN | POLLRDNORM); - if (TX_FLOW_IS_ON(cf_sk)) - mask |= (POLLOUT | POLLWRNORM); + err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); + if (err) { + kfree_skb(skb); + goto out_err; + } + err = transmit_skb(skb, cf_sk, + msg->msg_flags&MSG_DONTWAIT, timeo); + if (err < 0) { + kfree_skb(skb); + goto pipe_err; + } + sent += size; } - release_sock(&cf_sk->sk); - trace_printk("CAIF: %s(): poll mask=0x%04x\n", - __func__, mask); - return mask; -} - -static void drain_queue(struct caifsock *cf_sk) -{ - struct cfpkt *pkt = NULL; - - /* Empty the queue */ - do { - /* The queue has its own lock */ - if (!cf_sk->pktq) - break; - - pkt = cfpkt_dequeue(cf_sk->pktq); - if (!pkt) - break; - pr_debug("CAIF: %s(): freeing packet from read queue\n", - __func__); - cfpkt_destroy(pkt); - } while (1); + return sent; - cf_sk->read_queue_len = 0; +pipe_err: + if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL)) + send_sig(SIGPIPE, current, 0); + err = -EPIPE; +out_err: + return sent ? : err; } static int setsockopt(struct socket *sock, @@ -736,19 +738,13 @@ static int setsockopt(struct socket *sock, int prio, linksel; struct ifreq ifreq; - if (STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): setsockopt " - "cannot be done on a connected socket\n", - __func__); + if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED) return -ENOPROTOOPT; - } + switch (opt) { case CAIFSO_LINK_SELECT: - if (ol < sizeof(int)) { - pr_debug("CAIF: %s(): setsockopt" - " CAIFSO_CHANNEL_CONFIG bad size\n", __func__); + if (ol < sizeof(int)) return -EINVAL; - } if (lvl != SOL_CAIF) goto bad_sol; if (copy_from_user(&linksel, ov, sizeof(int))) @@ -761,28 +757,20 @@ static int setsockopt(struct socket *sock, case SO_PRIORITY: if (lvl != SOL_SOCKET) goto bad_sol; - if (ol < sizeof(int)) { - pr_debug("CAIF: %s(): setsockopt" - " SO_PRIORITY bad size\n", __func__); + if (ol < sizeof(int)) return -EINVAL; - } if (copy_from_user(&prio, ov, sizeof(int))) return -EINVAL; lock_sock(&(cf_sk->sk)); cf_sk->conn_req.priority = prio; - pr_debug("CAIF: %s(): Setting sockopt priority=%d\n", __func__, - cf_sk->conn_req.priority); release_sock(&cf_sk->sk); return 0; case SO_BINDTODEVICE: if (lvl != SOL_SOCKET) goto bad_sol; - if (ol < sizeof(struct ifreq)) { - pr_debug("CAIF: %s(): setsockopt" - " SO_PRIORITY bad size\n", __func__); + if (ol < sizeof(struct ifreq)) return -EINVAL; - } if (copy_from_user(&ifreq, ov, sizeof(ifreq))) return -EFAULT; lock_sock(&(cf_sk->sk)); @@ -798,359 +786,275 @@ static int setsockopt(struct socket *sock, goto bad_sol; if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL) return -ENOPROTOOPT; - if (ol > sizeof(cf_sk->conn_req.param.data)) - goto req_param_bad_size; - lock_sock(&(cf_sk->sk)); cf_sk->conn_req.param.size = ol; - if (copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) { + if (ol > sizeof(cf_sk->conn_req.param.data) || + copy_from_user(&cf_sk->conn_req.param.data, ov, ol)) { release_sock(&cf_sk->sk); -req_param_bad_size: - pr_debug("CAIF: %s(): setsockopt" - " CAIFSO_CHANNEL_CONFIG bad size\n", __func__); return -EINVAL; } - release_sock(&cf_sk->sk); return 0; default: - pr_debug("CAIF: %s(): unhandled option %d\n", __func__, opt); - return -EINVAL; + return -ENOPROTOOPT; } return 0; bad_sol: - pr_debug("CAIF: %s(): setsockopt bad level\n", __func__); return -ENOPROTOOPT; } -static int caif_connect(struct socket *sock, struct sockaddr *uservaddr, - int sockaddr_len, int flags) +/* + * caif_connect() - Connect a CAIF Socket + * Copied and modified af_irda.c:irda_connect(). + * + * Note : by consulting "errno", the user space caller may learn the cause + * of the failure. Most of them are visible in the function, others may come + * from subroutines called and are listed here : + * o -EAFNOSUPPORT: bad socket family or type. + * o -ESOCKTNOSUPPORT: bad socket type or protocol + * o -EINVAL: bad socket address, or CAIF link type + * o -ECONNREFUSED: remote end refused the connection. + * o -EINPROGRESS: connect request sent but timed out (or non-blocking) + * o -EISCONN: already connected. + * o -ETIMEDOUT: Connection timed out (send timeout) + * o -ENODEV: No link layer to send request + * o -ECONNRESET: Received Shutdown indication or lost link layer + * o -ENOMEM: Out of memory + * + * State Strategy: + * o sk_state: holds the CAIF_* protocol state, it's updated by + * caif_ctrl_cb. + * o sock->state: holds the SS_* socket state and is updated by connect and + * disconnect. + */ +static int caif_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) { - struct caifsock *cf_sk = NULL; - int result = -1; - int mode = 0; - int ret = -EIO; struct sock *sk = sock->sk; - BUG_ON(sk == NULL); - - cf_sk = container_of(sk, struct caifsock, sk); - - trace_printk("CAIF: %s(): cf_sk=%p OPEN=%d, TX_FLOW=%d, RX_FLOW=%d\n", - __func__, cf_sk, - STATE_IS_OPEN(cf_sk), - TX_FLOW_IS_ON(cf_sk), RX_FLOW_IS_ON(cf_sk)); - + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + long timeo; + int err; + lock_sock(sk); - if (sock->type == SOCK_SEQPACKET || sock->type == SOCK_STREAM) - sock->state = SS_CONNECTING; - else + err = -EAFNOSUPPORT; + if (uaddr->sa_family != AF_CAIF) goto out; - /* I want to be alone on cf_sk (except status and queue) */ - lock_sock(&(cf_sk->sk)); - - if (sockaddr_len != sizeof(struct sockaddr_caif)) { - pr_debug("CAIF: %s(): Bad address len (%ld,%lu)\n", - __func__, (long) sockaddr_len, - (long unsigned) sizeof(struct sockaddr_caif)); - ret = -EINVAL; - goto open_error; + err = -ESOCKTNOSUPPORT; + if (unlikely(!(sk->sk_type == SOCK_STREAM && + cf_sk->sk.sk_protocol == CAIFPROTO_AT) && + sk->sk_type != SOCK_SEQPACKET)) + goto out; + switch (sock->state) { + case SS_UNCONNECTED: + /* Normal case, a fresh connect */ + caif_assert(sk->sk_state == CAIF_DISCONNECTED); + break; + case SS_CONNECTING: + switch (sk->sk_state) { + case CAIF_CONNECTED: + sock->state = SS_CONNECTED; + err = -EISCONN; + goto out; + case CAIF_DISCONNECTED: + /* Reconnect allowed */ + break; + case CAIF_CONNECTING: + err = -EALREADY; + if (flags & O_NONBLOCK) + goto out; + goto wait_connect; + } + break; + case SS_CONNECTED: + caif_assert(sk->sk_state == CAIF_CONNECTED || + sk->sk_state == CAIF_DISCONNECTED); + if (sk->sk_shutdown & SHUTDOWN_MASK) { + /* Allow re-connect after SHUTDOWN_IND */ + caif_disconnect_client(&cf_sk->layer); + break; + } + /* No reconnect on a seqpacket socket */ + err = -EISCONN; + goto out; + case SS_DISCONNECTING: + case SS_FREE: + caif_assert(1); /*Should never happen */ + break; } + sk->sk_state = CAIF_DISCONNECTED; + sock->state = SS_UNCONNECTED; + sk_stream_kill_queues(&cf_sk->sk); - if (uservaddr->sa_family != AF_CAIF) { - pr_debug("CAIF: %s(): Bad address family (%d)\n", - __func__, uservaddr->sa_family); - ret = -EAFNOSUPPORT; - goto open_error; - } + err = -EINVAL; + if (addr_len != sizeof(struct sockaddr_caif) || + !uaddr) + goto out; - memcpy(&cf_sk->conn_req.sockaddr, uservaddr, + memcpy(&cf_sk->conn_req.sockaddr, uaddr, sizeof(struct sockaddr_caif)); - dbfs_atomic_inc(&cnt.num_open); - mode = SKT_READ_FLAG | SKT_WRITE_FLAG; - - /* If socket is not open, make sure socket is in fully closed state */ - if (!STATE_IS_OPEN(cf_sk)) { - /* Has link close response been received (if we ever sent it)?*/ - if (STATE_IS_PENDING(cf_sk)) { - /* - * Still waiting for close response from remote. - * If opened non-blocking, report "would block" - */ - if (flags & O_NONBLOCK) { - pr_debug("CAIF: %s(): O_NONBLOCK" - " && close pending\n", __func__); - ret = -EAGAIN; - goto open_error; - } - - pr_debug("CAIF: %s(): Wait for close response" - " from remote...\n", __func__); - - release_sock(&cf_sk->sk); - - /* - * Blocking mode; close is pending and we need to wait - * for its conclusion. - */ - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (1)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } - } + /* Move to connecting socket, start sending Connect Requests */ + sock->state = SS_CONNECTING; + sk->sk_state = CAIF_CONNECTING; + + dbfs_atomic_inc(&cnt.num_connect_req); + cf_sk->layer.receive = caif_sktrecv_cb; + err = caif_connect_client(&cf_sk->conn_req, + &cf_sk->layer); + if (err < 0) { + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; + goto out; } - /* socket is now either closed, pending open or open */ - if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) { - /* Open */ - pr_debug("CAIF: %s(): Socket is already opened (cf_sk=%p)" - " check access f_flags = 0x%x file_mode = 0x%x\n", - __func__, cf_sk, mode, cf_sk->file_mode); - - } else { - /* We are closed or pending open. - * If closed: send link setup - * If pending open: link setup already sent (we could have been - * interrupted by a signal last time) - */ - if (!STATE_IS_OPEN(cf_sk)) { - /* First opening of file; connect lower layers: */ - /* Drain queue (very unlikely) */ - drain_queue(cf_sk); - - cf_sk->layer.receive = caif_sktrecv_cb; - SET_STATE_OPEN(cf_sk); - SET_PENDING_ON(cf_sk); - - /* Register this channel. */ - result = - caif_connect_client(&cf_sk->conn_req, - &cf_sk->layer); - if (result < 0) { - pr_debug("CAIF: %s(): can't register channel\n", - __func__); - ret = -EIO; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - goto open_error; - } - dbfs_atomic_inc(&cnt.num_init); - } - - /* If opened non-blocking, report "success". - */ - if (flags & O_NONBLOCK) { - pr_debug("CAIF: %s(): O_NONBLOCK success\n", - __func__); - ret = -EINPROGRESS; - cf_sk->sk.sk_err = -EINPROGRESS; - goto open_error; - } - - trace_printk("CAIF: %s(): Wait for connect response\n", - __func__); + err = -EINPROGRESS; +wait_connect: - /* release lock before waiting */ - release_sock(&cf_sk->sk); - - result = - wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - - lock_sock(&(cf_sk->sk)); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (2)", __func__); - ret = -ERESTARTSYS; - goto open_error; - } - - if (!STATE_IS_OPEN(cf_sk)) { - /* Lower layers said "no" */ - pr_debug("CAIF: %s(): Closed received\n", __func__); - ret = -EPIPE; - goto open_error; - } + if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK)) + goto out; - trace_printk("CAIF: %s(): Connect received\n", __func__); + timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + + release_sock(sk); + err = wait_event_interruptible_timeout(*sk_sleep(sk), + sk->sk_state != CAIF_CONNECTING, + timeo); + lock_sock(sk); + if (err < 0) + goto out; /* -ERESTARTSYS */ + if (err == 0 && sk->sk_state != CAIF_CONNECTED) { + err = -ETIMEDOUT; + goto out; } - /* Open is ok */ - cf_sk->file_mode |= mode; - trace_printk("CAIF: %s(): Connected - file mode = %x\n", - __func__, cf_sk->file_mode); - - release_sock(&cf_sk->sk); - return 0; -open_error: - sock->state = SS_UNCONNECTED; - release_sock(&cf_sk->sk); + if (sk->sk_state != CAIF_CONNECTED) { + sock->state = SS_UNCONNECTED; + err = sock_error(sk); + if (!err) + err = -ECONNREFUSED; + goto out; + } + sock->state = SS_CONNECTED; + err = 0; out: - return ret; + release_sock(sk); + return err; } -static int caif_shutdown(struct socket *sock, int how) + +/* + * caif_release() - Disconnect a CAIF Socket + * Copied and modified af_irda.c:irda_release(). + */ +static int caif_release(struct socket *sock) { - struct caifsock *cf_sk = NULL; - int result = 0; - int tx_flow_state_was_on; struct sock *sk = sock->sk; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); + int res = 0; - trace_printk("CAIF: %s(): enter\n", __func__); - pr_debug("f_flags=%x\n", sock->file->f_flags); - - if (how != SHUT_RDWR) - return -EOPNOTSUPP; - - cf_sk = container_of(sk, struct caifsock, sk); - if (cf_sk == NULL) { - pr_debug("CAIF: %s(): COULD NOT FIND SOCKET\n", __func__); - return -EBADF; - } - - /* I want to be alone on cf_sk (except status queue) */ - lock_sock(&(cf_sk->sk)); - sock_hold(&cf_sk->sk); - - /* IS_CLOSED have double meaning: - * 1) Spontanous Remote Shutdown Request. - * 2) Ack on a channel teardown(disconnect) - * Must clear bit in case we previously received - * remote shudown request. - */ - if (STATE_IS_OPEN(cf_sk) && !STATE_IS_PENDING(cf_sk)) { - SET_STATE_CLOSED(cf_sk); - SET_PENDING_ON(cf_sk); - tx_flow_state_was_on = TX_FLOW_IS_ON(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - - /* Hold the socket until DEINIT_RSP is received */ - sock_hold(&cf_sk->sk); - result = caif_disconnect_client(&cf_sk->layer); - - if (result < 0) { - pr_debug("CAIF: %s(): " - "caif_disconnect_client() failed\n", - __func__); - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - release_sock(&cf_sk->sk); - sock_put(&cf_sk->sk); - return -EIO; - } + if (!sk) + return 0; - } - if (STATE_IS_REMOTE_SHUTDOWN(cf_sk)) { - SET_PENDING_OFF(cf_sk); - SET_REMOTE_SHUTDOWN_OFF(cf_sk); - } + set_tx_flow_off(cf_sk); /* - * Socket is no longer in state pending close, - * and we can release the reference. + * Ensure that packets are not queued after this point in time. + * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock, + * this ensures no packets when sock is dead. */ + spin_lock(&sk->sk_receive_queue.lock); + sock_set_flag(sk, SOCK_DEAD); + spin_unlock(&sk->sk_receive_queue.lock); + sock->sk = NULL; - dbfs_atomic_inc(&cnt.num_close); - drain_queue(cf_sk); - SET_RX_FLOW_ON(cf_sk); - cf_sk->file_mode = 0; - sock_put(&cf_sk->sk); - release_sock(&cf_sk->sk); - if (!result && (sock->file->f_flags & O_NONBLOCK)) { - pr_debug("nonblocking shutdown returing -EAGAIN\n"); - return -EAGAIN; - } else - return result; -} - -static ssize_t caif_sock_no_sendpage(struct socket *sock, - struct page *page, - int offset, size_t size, int flags) -{ - return -EOPNOTSUPP; -} - -/* This function is called as part of close. */ -static int caif_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - struct caifsock *cf_sk = NULL; - int res; - caif_assert(sk != NULL); - cf_sk = container_of(sk, struct caifsock, sk); + dbfs_atomic_inc(&cnt.num_disconnect); if (cf_sk->debugfs_socket_dir != NULL) debugfs_remove_recursive(cf_sk->debugfs_socket_dir); - res = caif_shutdown(sock, SHUT_RDWR); - if (res && res != -EINPROGRESS) - return res; - - /* - * FIXME: Shutdown should probably be possible to do async - * without flushing queues, allowing reception of frames while - * waiting for DEINIT_IND. - * Release should always block, to allow secure decoupling of - * CAIF stack. - */ - if (!(sock->file->f_flags & O_NONBLOCK)) { - res = wait_event_interruptible(*cf_sk->sk.sk_sleep, - !STATE_IS_PENDING(cf_sk)); - - if (res == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible" - "woken by a signal (1)", __func__); - } - } lock_sock(&(cf_sk->sk)); + sk->sk_state = CAIF_DISCONNECTED; + sk->sk_shutdown = SHUTDOWN_MASK; - sock->sk = NULL; + if (cf_sk->sk.sk_socket->state == SS_CONNECTED || + cf_sk->sk.sk_socket->state == SS_CONNECTING) + res = caif_disconnect_client(&cf_sk->layer); - /* Detach the socket from its process context by making it orphan. */ - sock_orphan(sk); + cf_sk->sk.sk_socket->state = SS_DISCONNECTING; + wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP); - /* - * Setting SHUTDOWN_MASK means that both send and receive are shutdown - * for the socket. - */ - sk->sk_shutdown = SHUTDOWN_MASK; + sock_orphan(sk); + cf_sk->layer.dn = NULL; + sk_stream_kill_queues(&cf_sk->sk); + release_sock(sk); + sock_put(sk); + return res; +} - /* - * Set the socket state to closed, the TCP_CLOSE macro is used when - * closing any socket. - */ +/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */ +static unsigned int caif_poll(struct file *file, + struct socket *sock, poll_table *wait) +{ + struct sock *sk = sock->sk; + unsigned int mask; + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); - /* Flush out this sockets receive queue. */ - drain_queue(cf_sk); + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; - /* Finally release the socket. */ - SET_STATE_PENDING_DESTROY(cf_sk); + /* exceptional events? */ + if (sk->sk_err) + mask |= POLLERR; + if (sk->sk_shutdown == SHUTDOWN_MASK) + mask |= POLLHUP; + if (sk->sk_shutdown & RCV_SHUTDOWN) + mask |= POLLRDHUP; - release_sock(&cf_sk->sk); + /* readable? */ + if (!skb_queue_empty(&sk->sk_receive_queue) || + (sk->sk_shutdown & RCV_SHUTDOWN)) + mask |= POLLIN | POLLRDNORM; - sock_put(sk); + /* Connection-based need to check for termination and startup */ + if (sk->sk_state == CAIF_DISCONNECTED) + mask |= POLLHUP; /* - * The rest of the cleanup will be handled from the - * caif_sock_destructor + * we set writable also when the other side has shut down the + * connection. This prevents stuck sockets. */ - return res; + if (sock_writeable(sk) && tx_flow_is_on(cf_sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + + return mask; } -static const struct proto_ops caif_ops = { +static const struct proto_ops caif_seqpacket_ops = { + .family = PF_CAIF, + .owner = THIS_MODULE, + .release = caif_release, + .bind = sock_no_bind, + .connect = caif_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = caif_poll, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = caif_seqpkt_sendmsg, + .recvmsg = caif_seqpkt_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static const struct proto_ops caif_stream_ops = { .family = PF_CAIF, .owner = THIS_MODULE, .release = caif_release, @@ -1162,73 +1066,62 @@ static const struct proto_ops caif_ops = { .poll = caif_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, - .shutdown = caif_shutdown, + .shutdown = sock_no_shutdown, .setsockopt = setsockopt, .getsockopt = sock_no_getsockopt, - .sendmsg = caif_sendmsg, - .recvmsg = caif_recvmsg, + .sendmsg = caif_stream_sendmsg, + .recvmsg = caif_stream_recvmsg, .mmap = sock_no_mmap, - .sendpage = caif_sock_no_sendpage, + .sendpage = sock_no_sendpage, }; /* This function is called when a socket is finally destroyed. */ static void caif_sock_destructor(struct sock *sk) { - struct caifsock *cf_sk = NULL; - cf_sk = container_of(sk, struct caifsock, sk); - /* Error checks. */ + struct caifsock *cf_sk = container_of(sk, struct caifsock, sk); caif_assert(!atomic_read(&sk->sk_wmem_alloc)); caif_assert(sk_unhashed(sk)); caif_assert(!sk->sk_socket); if (!sock_flag(sk, SOCK_DEAD)) { - pr_debug("CAIF: %s(): 0x%p", __func__, sk); + pr_info("Attempt to release alive CAIF socket: %p\n", sk); return; } - - if (STATE_IS_OPEN(cf_sk)) { - pr_debug("CAIF: %s(): socket is opened (cf_sk=%p)" - " file_mode = 0x%x\n", __func__, - cf_sk, cf_sk->file_mode); - return; - } - drain_queue(cf_sk); - kfree(cf_sk->pktq); - - trace_printk("CAIF: %s(): caif_sock_destructor: Removing socket %s\n", - __func__, cf_sk->name); - atomic_dec(&caif_nr_socks); + sk_stream_kill_queues(&cf_sk->sk); + dbfs_atomic_dec(&cnt.caif_nr_socks); } static int caif_create(struct net *net, struct socket *sock, int protocol, - int kern) + int kern) { struct sock *sk = NULL; struct caifsock *cf_sk = NULL; - int result = 0; static struct proto prot = {.name = "PF_CAIF", .owner = THIS_MODULE, .obj_size = sizeof(struct caifsock), }; + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN)) + return -EPERM; /* * The sock->type specifies the socket type to use. - * in SEQPACKET mode packet boundaries are enforced. + * The CAIF socket is a packet stream in the sense + * that it is packet based. CAIF trusts the reliability + * of the link, no resending is implemented. */ - if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) + if (sock->type == SOCK_SEQPACKET) + sock->ops = &caif_seqpacket_ops; + else if (sock->type == SOCK_STREAM) + sock->ops = &caif_stream_ops; + else return -ESOCKTNOSUPPORT; - if (net != &init_net) - return -EAFNOSUPPORT; - if (protocol < 0 || protocol >= CAIFPROTO_MAX) return -EPROTONOSUPPORT; /* - * Set the socket state to unconnected. The socket state is really - * not used at all in the net/core or socket.c but the + * Set the socket state to unconnected. The socket state + * is really not used at all in the net/core or socket.c but the * initialization makes sure that sock->state is not uninitialized. */ - sock->state = SS_UNCONNECTED; - sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot); if (!sk) return -ENOMEM; @@ -1238,11 +1131,9 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Store the protocol */ sk->sk_protocol = (unsigned char) protocol; - spin_lock_init(&cf_sk->read_queue_len_lock); - - /* Fill in some information concerning the misc socket. */ - snprintf(cf_sk->name, sizeof(cf_sk->name), "cf_sk%d", - atomic_read(&caif_nr_socks)); + /* Sendbuf dictates the amount of outbound packets not yet sent */ + sk->sk_sndbuf = CAIF_DEF_SNDBUF; + sk->sk_rcvbuf = CAIF_DEF_RCVBUF; /* * Lock in order to try to stop someone from opening the socket @@ -1252,108 +1143,85 @@ static int caif_create(struct net *net, struct socket *sock, int protocol, /* Initialize the nozero default sock structure data. */ sock_init_data(sock, sk); - sock->ops = &caif_ops; sk->sk_destruct = caif_sock_destructor; - sk->sk_sndbuf = caif_sockbuf_size; - sk->sk_rcvbuf = caif_sockbuf_size; - cf_sk->pktq = cfpktq_create(); + mutex_init(&cf_sk->readlock); /* single task reading lock */ + cf_sk->layer.ctrlcmd = caif_ctrl_cb; + cf_sk->sk.sk_socket->state = SS_UNCONNECTED; + cf_sk->sk.sk_state = CAIF_DISCONNECTED; - if (!cf_sk->pktq) { - pr_err("CAIF: %s(): queue create failed.\n", __func__); - result = -ENOMEM; - release_sock(&cf_sk->sk); - goto err_failed; - } - cf_sk->layer.ctrlcmd = caif_sktflowctrl_cb; - SET_STATE_CLOSED(cf_sk); - SET_PENDING_OFF(cf_sk); - SET_TX_FLOW_OFF(cf_sk); - SET_RX_FLOW_ON(cf_sk); + set_tx_flow_off(cf_sk); + set_rx_flow_on(cf_sk); /* Set default options on configuration */ cf_sk->conn_req.priority = CAIF_PRIO_NORMAL; - cf_sk->conn_req.link_selector = CAIF_LINK_HIGH_BANDW; + cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY; cf_sk->conn_req.protocol = protocol; /* Increase the number of sockets created. */ - atomic_inc(&caif_nr_socks); + dbfs_atomic_inc(&cnt.caif_nr_socks); +#ifdef CONFIG_DEBUG_FS if (!IS_ERR(debugfsdir)) { + /* Fill in some information concerning the misc socket. */ + snprintf(cf_sk->name, sizeof(cf_sk->name), "cfsk%d", + atomic_read(&cnt.caif_nr_socks)); + cf_sk->debugfs_socket_dir = debugfs_create_dir(cf_sk->name, debugfsdir); - debugfs_create_u32("conn_state", S_IRUSR | S_IWUSR, - cf_sk->debugfs_socket_dir, &cf_sk->conn_state); + debugfs_create_u32("sk_state", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_state); debugfs_create_u32("flow_state", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, &cf_sk->flow_state); - debugfs_create_u32("read_queue_len", S_IRUSR | S_IWUSR, + debugfs_create_u32("sk_rmem_alloc", S_IRUSR | S_IWUSR, + cf_sk->debugfs_socket_dir, + (u32 *) &cf_sk->sk.sk_rmem_alloc); + debugfs_create_u32("sk_wmem_alloc", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, - (u32 *) &cf_sk->read_queue_len); + (u32 *) &cf_sk->sk.sk_wmem_alloc); debugfs_create_u32("identity", S_IRUSR | S_IWUSR, cf_sk->debugfs_socket_dir, (u32 *) &cf_sk->layer.id); } +#endif release_sock(&cf_sk->sk); return 0; -err_failed: - sk_free(sk); - return result; } + static struct net_proto_family caif_family_ops = { .family = PF_CAIF, .create = caif_create, .owner = THIS_MODULE, }; -static int af_caif_init(void) +int af_caif_init(void) { - int err; - err = sock_register(&caif_family_ops); - + int err = sock_register(&caif_family_ops); if (!err) return err; - return 0; } static int __init caif_sktinit_module(void) { - int stat; #ifdef CONFIG_DEBUG_FS - debugfsdir = debugfs_create_dir("chnl_skt", NULL); + debugfsdir = debugfs_create_dir("caif_sk", NULL); if (!IS_ERR(debugfsdir)) { - debugfs_create_u32("skb_inuse", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_in_use); - debugfs_create_u32("skb_alloc", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_alloc); - debugfs_create_u32("skb_free", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.skb_free); debugfs_create_u32("num_sockets", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &caif_nr_socks); - debugfs_create_u32("num_open", S_IRUSR | S_IWUSR, + (u32 *) &cnt.caif_nr_socks); + debugfs_create_u32("num_connect_req", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_open); - debugfs_create_u32("num_close", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_req); + debugfs_create_u32("num_connect_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_close); - debugfs_create_u32("num_init", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_resp); + debugfs_create_u32("num_connect_fail_resp", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init); - debugfs_create_u32("num_init_resp", S_IRUSR | S_IWUSR, + (u32 *) &cnt.num_connect_fail_resp); + debugfs_create_u32("num_disconnect", S_IRUSR | S_IWUSR, debugfsdir, - (u32 *) &cnt.num_init_resp); - debugfs_create_u32("num_init_fail_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_init_fail_resp); - debugfs_create_u32("num_deinit", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit); - debugfs_create_u32("num_deinit_resp", S_IRUSR | S_IWUSR, - debugfsdir, - (u32 *) &cnt.num_deinit_resp); + (u32 *) &cnt.num_disconnect); debugfs_create_u32("num_remote_shutdown_ind", S_IRUSR | S_IWUSR, debugfsdir, (u32 *) &cnt.num_remote_shutdown_ind); @@ -1371,13 +1239,7 @@ static int __init caif_sktinit_module(void) (u32 *) &cnt.num_rx_flow_on); } #endif - stat = af_caif_init(); - if (stat) { - pr_err("CAIF: %s(): Failed to initialize CAIF socket layer.", - __func__); - return stat; - } - return 0; + return af_caif_init(); } static void __exit caif_sktexit_module(void) @@ -1386,6 +1248,5 @@ static void __exit caif_sktexit_module(void) if (debugfsdir != NULL) debugfs_remove_recursive(debugfsdir); } - module_init(caif_sktinit_module); module_exit(caif_sktexit_module); diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c index c873e3d4387c..471c62939fad 100644 --- a/net/caif/cfcnfg.c +++ b/net/caif/cfcnfg.c @@ -51,12 +51,11 @@ struct cfcnfg { struct cfcnfg_phyinfo phy_layers[MAX_PHY_LAYERS]; }; -static void cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, +static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, u8 phyid, struct cflayer *adapt_layer); -static void cncfg_linkdestroy_rsp(struct cflayer *layer, u8 linkid, - struct cflayer *client_layer); -static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid, +static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id); +static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, struct cflayer *adapt_layer); static void cfctrl_resp_func(void); static void cfctrl_enum_resp(void); @@ -82,13 +81,13 @@ struct cfcnfg *cfcnfg_create(void) resp = cfctrl_get_respfuncs(this->ctrl); resp->enum_rsp = cfctrl_enum_resp; resp->linkerror_ind = cfctrl_resp_func; - resp->linkdestroy_rsp = cncfg_linkdestroy_rsp; + resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp; resp->sleep_rsp = cfctrl_resp_func; resp->wake_rsp = cfctrl_resp_func; resp->restart_rsp = cfctrl_resp_func; resp->radioset_rsp = cfctrl_resp_func; - resp->linksetup_rsp = cncfg_linkup_rsp; - resp->reject_rsp = cncfg_reject_rsp; + resp->linksetup_rsp = cfcnfg_linkup_rsp; + resp->reject_rsp = cfcnfg_reject_rsp; this->last_phyid = 1; @@ -175,142 +174,82 @@ int cfcnfg_get_named(struct cfcnfg *cnfg, char *name) return 0; } -/* - * NOTE: What happens on destroy failure: - * 1a) No response - Too early - * This will not happen because enumerate has already - * completed. - * 1b) No response - FATAL - * Not handled, but this should be a CAIF PROTOCOL ERROR - * Modem error, response is really expected - this - * case is not really handled. - * 2) O/E-bit indicate error - * Ignored - this link is destroyed anyway. - * 3) Not able to match on request - * Not handled, but this should be a CAIF PROTOCOL ERROR - * 4) Link-Error - (no response) - * Not handled, but this should be a CAIF PROTOCOL ERROR - */ - -int cfcnfg_del_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) +int cfcnfg_disconn_adapt_layer(struct cfcnfg *cnfg, struct cflayer *adap_layer) { u8 channel_id = 0; int ret = 0; + struct cflayer *servl = NULL; struct cfcnfg_phyinfo *phyinfo = NULL; u8 phyid = 0; - caif_assert(adap_layer != NULL); channel_id = adap_layer->id; - if (channel_id == 0) { + if (adap_layer->dn == NULL || channel_id == 0) { pr_err("CAIF: %s():adap_layer->id is 0\n", __func__); ret = -ENOTCONN; goto end; } - - if (adap_layer->dn == NULL) { - pr_err("CAIF: %s():adap_layer->dn is NULL\n", __func__); - ret = -ENODEV; - goto end; - } - - if (adap_layer->dn != NULL) - phyid = cfsrvl_getphyid(adap_layer->dn); - - phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); - if (phyinfo == NULL) { - pr_warning("CAIF: %s(): No interface to send disconnect to\n", - __func__); - ret = -ENODEV; + servl = cfmuxl_remove_uplayer(cnfg->mux, channel_id); + if (servl == NULL) goto end; - } - - if (phyinfo->id != phyid - || phyinfo->phy_layer->id != phyid - || phyinfo->frm_layer->id != phyid) { - - pr_err("CAIF: %s(): Inconsistency in phy registration\n", - __func__); + layer_set_up(servl, NULL); + ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); + if (servl == NULL) { + pr_err("CAIF: %s(): PROTOCOL ERROR " + "- Error removing service_layer Channel_Id(%d)", + __func__, channel_id); ret = -EINVAL; goto end; } + caif_assert(channel_id == servl->id); + if (adap_layer->dn != NULL) { + phyid = cfsrvl_getphyid(adap_layer->dn); - ret = cfctrl_linkdown_req(cnfg->ctrl, channel_id, adap_layer); - -end: + phyinfo = cfcnfg_get_phyinfo(cnfg, phyid); + if (phyinfo == NULL) { + pr_warning("CAIF: %s(): " + "No interface to send disconnect to\n", + __func__); + ret = -ENODEV; + goto end; + } + if (phyinfo->id != phyid || + phyinfo->phy_layer->id != phyid || + phyinfo->frm_layer->id != phyid) { + pr_err("CAIF: %s(): " + "Inconsistency in phy registration\n", + __func__); + ret = -EINVAL; + goto end; + } + } if (phyinfo != NULL && --phyinfo->phy_ref_count == 0 && phyinfo->phy_layer != NULL && phyinfo->phy_layer->modemcmd != NULL) { phyinfo->phy_layer->modemcmd(phyinfo->phy_layer, _CAIF_MODEMCMD_PHYIF_USELESS); } +end: + cfsrvl_put(servl); + cfctrl_cancel_req(cnfg->ctrl, adap_layer); + if (adap_layer->ctrlcmd != NULL) + adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); return ret; } -EXPORT_SYMBOL(cfcnfg_del_adapt_layer); +EXPORT_SYMBOL(cfcnfg_disconn_adapt_layer); -static void cncfg_linkdestroy_rsp(struct cflayer *layer, u8 linkid, - struct cflayer *client_layer) +void cfcnfg_release_adap_layer(struct cflayer *adap_layer) { - struct cfcnfg *cnfg = container_obj(layer); - struct cflayer *servl; - - /* - * 1) Remove service from the MUX layer. The MUX must - * guarante that no more payload sent "upwards" (receive) - */ - servl = cfmuxl_remove_uplayer(cnfg->mux, linkid); - - if (servl == NULL) { - pr_err("CAIF: %s(): PROTOCOL ERROR " - "- Error removing service_layer Linkid(%d)", - __func__, linkid); - return; - } - caif_assert(linkid == servl->id); - - if (servl != client_layer && servl->up != client_layer) { - pr_err("CAIF: %s(): Error removing service_layer " - "Linkid(%d) %p %p", - __func__, linkid, (void *) servl, - (void *) client_layer); - return; - } - - /* - * 2) DEINIT_RSP must guarantee that no more packets are transmitted - * from client (adap_layer) when it returns. - */ - - if (servl->ctrlcmd == NULL) { - pr_err("CAIF: %s(): Error servl->ctrlcmd == NULL", __func__); - return; - } - - servl->ctrlcmd(servl, CAIF_CTRLCMD_DEINIT_RSP, 0); - - /* 3) It is now safe to destroy the service layer. */ - cfservl_destroy(servl); + if (adap_layer->dn) + cfsrvl_put(adap_layer->dn); } +EXPORT_SYMBOL(cfcnfg_release_adap_layer); -/* - * NOTE: What happens on linksetup failure: - * 1a) No response - Too early - * This will not happen because enumerate is secured - * before using interface. - * 1b) No response - FATAL - * Not handled, but this should be a CAIF PROTOCOL ERROR - * Modem error, response is really expected - this case is - * not really handled. - * 2) O/E-bit indicate error - * Handled in cnfg_reject_rsp - * 3) Not able to match on request - * Not handled, but this should be a CAIF PROTOCOL ERROR - * 4) Link-Error - (no response) - * Not handled, but this should be a CAIF PROTOCOL ERROR - */ +static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) +{ +} -int -cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, +int cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, struct cfctrl_link_param *param, struct cflayer *adap_layer) { @@ -340,12 +279,11 @@ cfcnfg_add_adaptation_layer(struct cfcnfg *cnfg, param->phyid); /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ cfctrl_enum_req(cnfg->ctrl, param->phyid); - cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); - return 0; + return cfctrl_linkup_request(cnfg->ctrl, param, adap_layer); } EXPORT_SYMBOL(cfcnfg_add_adaptation_layer); -static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid, +static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, struct cflayer *adapt_layer) { if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) @@ -354,15 +292,17 @@ static void cncfg_reject_rsp(struct cflayer *layer, u8 linkid, } static void -cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv, +cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, u8 phyid, struct cflayer *adapt_layer) { struct cfcnfg *cnfg = container_obj(layer); struct cflayer *servicel = NULL; struct cfcnfg_phyinfo *phyinfo; if (adapt_layer == NULL) { - pr_err("CAIF: %s(): PROTOCOL ERROR " - "- LinkUp Request/Response did not match\n", __func__); + pr_debug("CAIF: %s(): link setup response " + "but no client exist, send linkdown back\n", + __func__); + cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); return; } @@ -383,26 +323,26 @@ cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv, _CAIF_MODEMCMD_PHYIF_USEFULL); } - adapt_layer->id = linkid; + adapt_layer->id = channel_id; switch (serv) { case CFCTRL_SRV_VEI: - servicel = cfvei_create(linkid, &phyinfo->dev_info); + servicel = cfvei_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_DATAGRAM: - servicel = cfdgml_create(linkid, &phyinfo->dev_info); + servicel = cfdgml_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_RFM: - servicel = cfrfml_create(linkid, &phyinfo->dev_info); + servicel = cfrfml_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_UTIL: - servicel = cfutill_create(linkid, &phyinfo->dev_info); + servicel = cfutill_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_VIDEO: - servicel = cfvidl_create(linkid, &phyinfo->dev_info); + servicel = cfvidl_create(channel_id, &phyinfo->dev_info); break; case CFCTRL_SRV_DBG: - servicel = cfdbgl_create(linkid, &phyinfo->dev_info); + servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); break; default: pr_err("CAIF: %s(): Protocol error. " @@ -415,9 +355,10 @@ cncfg_linkup_rsp(struct cflayer *layer, u8 linkid, enum cfctrl_srv serv, return; } layer_set_dn(servicel, cnfg->mux); - cfmuxl_set_uplayer(cnfg->mux, servicel, linkid); + cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); layer_set_up(servicel, adapt_layer); layer_set_dn(adapt_layer, servicel); + cfsrvl_get(servicel); servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); } diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c index 11f80140f3cb..a521d32cfe56 100644 --- a/net/caif/cfctrl.c +++ b/net/caif/cfctrl.c @@ -32,6 +32,7 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, struct cflayer *cfctrl_create(void) { + struct dev_info dev_info; struct cfctrl *this = kmalloc(sizeof(struct cfctrl), GFP_ATOMIC); if (!this) { @@ -39,12 +40,13 @@ struct cflayer *cfctrl_create(void) return NULL; } caif_assert(offsetof(struct cfctrl, serv.layer) == 0); + memset(&dev_info, 0, sizeof(dev_info)); + dev_info.id = 0xff; memset(this, 0, sizeof(*this)); + cfsrvl_init(&this->serv, 0, &dev_info); spin_lock_init(&this->info_list_lock); atomic_set(&this->req_seq_no, 1); atomic_set(&this->rsp_seq_no, 1); - this->serv.dev_info.id = 0xff; - this->serv.layer.id = 0; this->serv.layer.receive = cfctrl_recv; sprintf(this->serv.layer.name, "ctrl"); this->serv.layer.ctrlcmd = cfctrl_ctrlcmd; @@ -127,20 +129,6 @@ void cfctrl_insert_req(struct cfctrl *ctrl, spin_unlock(&ctrl->info_list_lock); } -static void cfctrl_insert_req2(struct cfctrl *ctrl, enum cfctrl_cmd cmd, - u8 linkid, struct cflayer *user_layer) -{ - struct cfctrl_request_info *req = kmalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - pr_warning("CAIF: %s(): Out of memory\n", __func__); - return; - } - req->client_layer = user_layer; - req->cmd = cmd; - req->channel_id = linkid; - cfctrl_insert_req(ctrl, req); -} - /* Compare and remove request */ struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl, struct cfctrl_request_info *req) @@ -234,7 +222,7 @@ void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid) } } -void cfctrl_linkup_request(struct cflayer *layer, +int cfctrl_linkup_request(struct cflayer *layer, struct cfctrl_link_param *param, struct cflayer *user_layer) { @@ -248,7 +236,7 @@ void cfctrl_linkup_request(struct cflayer *layer, struct cfpkt *pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN); if (!pkt) { pr_warning("CAIF: %s(): Out of memory\n", __func__); - return; + return -ENOMEM; } cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP); cfpkt_addbdy(pkt, (param->chtype << 4) + param->linktype); @@ -294,11 +282,12 @@ void cfctrl_linkup_request(struct cflayer *layer, default: pr_warning("CAIF: %s():Request setup of bad link type = %d\n", __func__, param->linktype); + return -EINVAL; } req = kmalloc(sizeof(*req), GFP_KERNEL); if (!req) { pr_warning("CAIF: %s(): Out of memory\n", __func__); - return; + return -ENOMEM; } memset(req, 0, sizeof(*req)); req->client_layer = user_layer; @@ -306,6 +295,11 @@ void cfctrl_linkup_request(struct cflayer *layer, req->param = *param; cfctrl_insert_req(cfctrl, req); init_info(cfpkt_info(pkt), cfctrl); + /* + * NOTE:Always send linkup and linkdown request on the same + * device as the payload. Otherwise old queued up payload + * might arrive with the newly allocated channel ID. + */ cfpkt_info(pkt)->dev_info->id = param->phyid; ret = cfctrl->serv.layer.dn->transmit(cfctrl->serv.layer.dn, pkt); @@ -313,7 +307,9 @@ void cfctrl_linkup_request(struct cflayer *layer, pr_err("CAIF: %s(): Could not transmit linksetup request\n", __func__); cfpkt_destroy(pkt); + return -ENODEV; } + return 0; } int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, @@ -326,7 +322,6 @@ int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid, pr_warning("CAIF: %s(): Out of memory\n", __func__); return -ENOMEM; } - cfctrl_insert_req2(cfctrl, CFCTRL_CMD_LINK_DESTROY, channelid, client); cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY); cfpkt_addbdy(pkt, channelid); init_info(cfpkt_info(pkt), cfctrl); @@ -392,6 +387,38 @@ void cfctrl_getstartreason_req(struct cflayer *layer) } +void cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer) +{ + struct cfctrl_request_info *p, *req; + struct cfctrl *ctrl = container_obj(layr); + spin_lock(&ctrl->info_list_lock); + + if (ctrl->first_req == NULL) { + spin_unlock(&ctrl->info_list_lock); + return; + } + + if (ctrl->first_req->client_layer == adap_layer) { + + req = ctrl->first_req; + ctrl->first_req = ctrl->first_req->next; + kfree(req); + } + + p = ctrl->first_req; + while (p != NULL && p->next != NULL) { + if (p->next->client_layer == adap_layer) { + + req = p->next; + p->next = p->next->next; + kfree(p->next); + } + p = p->next; + } + + spin_unlock(&ctrl->info_list_lock); +} + static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) { u8 cmdrsp; @@ -409,11 +436,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) cmd = cmdrsp & CFCTRL_CMD_MASK; if (cmd != CFCTRL_CMD_LINK_ERR && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)) { - if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE) { - pr_info("CAIF: %s() CAIF Protocol error:" - "Response bit not set\n", __func__); - goto error; - } + if (handle_loop(cfctrl, cmd, pkt) == CAIF_FAILURE) + cmdrsp |= CFCTRL_ERR_BIT; } switch (cmd) { @@ -451,12 +475,16 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) switch (serv) { case CFCTRL_SRV_VEI: case CFCTRL_SRV_DBG: + if (CFCTRL_ERR_BIT & cmdrsp) + break; /* Link ID */ cfpkt_extr_head(pkt, &linkid, 1); break; case CFCTRL_SRV_VIDEO: cfpkt_extr_head(pkt, &tmp, 1); linkparam.u.video.connid = tmp; + if (CFCTRL_ERR_BIT & cmdrsp) + break; /* Link ID */ cfpkt_extr_head(pkt, &linkid, 1); break; @@ -465,6 +493,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) cfpkt_extr_head(pkt, &tmp32, 4); linkparam.u.datagram.connid = le32_to_cpu(tmp32); + if (CFCTRL_ERR_BIT & cmdrsp) + break; /* Link ID */ cfpkt_extr_head(pkt, &linkid, 1); break; @@ -483,6 +513,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) *cp++ = tmp; *cp = '\0'; + if (CFCTRL_ERR_BIT & cmdrsp) + break; /* Link ID */ cfpkt_extr_head(pkt, &linkid, 1); @@ -519,6 +551,8 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) cfpkt_extr_head(pkt, &tmp, 1); *cp++ = tmp; } + if (CFCTRL_ERR_BIT & cmdrsp) + break; /* Link ID */ cfpkt_extr_head(pkt, &linkid, 1); /* Length */ @@ -560,13 +594,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt) break; case CFCTRL_CMD_LINK_DESTROY: cfpkt_extr_head(pkt, &linkid, 1); - rsp.cmd = cmd; - rsp.channel_id = linkid; - req = cfctrl_remove_req(cfctrl, &rsp); - cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid, - req ? req->client_layer : NULL); - if (req != NULL) - kfree(req); + cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid); break; case CFCTRL_CMD_LINK_ERR: pr_err("CAIF: %s(): Frame Error Indication received\n", @@ -608,7 +636,7 @@ static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, case CAIF_CTRLCMD_FLOW_OFF_IND: spin_lock(&this->info_list_lock); if (this->first_req != NULL) { - pr_warning("CAIF: %s(): Received flow off in " + pr_debug("CAIF: %s(): Received flow off in " "control layer", __func__); } spin_unlock(&this->info_list_lock); @@ -633,6 +661,7 @@ static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt) if (!ctrl->loop_linkused[linkid]) goto found; spin_unlock(&ctrl->loop_linkid_lock); + pr_err("CAIF: %s(): Out of link-ids\n", __func__); return -EINVAL; found: if (!ctrl->loop_linkused[linkid]) diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c index 6fb9f9e96cf8..7372f27f1d32 100644 --- a/net/caif/cfmuxl.c +++ b/net/caif/cfmuxl.c @@ -62,6 +62,7 @@ int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) { struct cfmuxl *muxl = container_obj(layr); spin_lock(&muxl->receive_lock); + cfsrvl_get(up); list_add(&up->node, &muxl->srvl_list); spin_unlock(&muxl->receive_lock); return 0; @@ -172,8 +173,11 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) struct cfmuxl *muxl = container_obj(layr); spin_lock(&muxl->receive_lock); up = get_up(muxl, id); + if (up == NULL) + return NULL; memset(muxl->up_cache, 0, sizeof(muxl->up_cache)); list_del(&up->node); + cfsrvl_put(up); spin_unlock(&muxl->receive_lock); return up; } @@ -203,8 +207,9 @@ static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) */ return /* CFGLU_EPROT; */ 0; } - + cfsrvl_get(up); ret = up->receive(up, pkt); + cfsrvl_put(up); return ret; } diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index d470c51c6431..aff31f34528f 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -158,6 +158,13 @@ void cfsrvl_init(struct cfsrvl *service, service->layer.ctrlcmd = cfservl_ctrlcmd; service->layer.modemcmd = cfservl_modemcmd; service->dev_info = *dev_info; + kref_init(&service->ref); +} + +void cfsrvl_release(struct kref *kref) +{ + struct cfsrvl *service = container_of(kref, struct cfsrvl, ref); + kfree(service); } bool cfsrvl_ready(struct cfsrvl *service, int *err) diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c index f622ff1d39ba..610966abe2dc 100644 --- a/net/caif/chnl_net.c +++ b/net/caif/chnl_net.c @@ -22,10 +22,10 @@ #include <net/caif/cfpkt.h> #include <net/caif/caif_dev.h> -#define CAIF_CONNECT_TIMEOUT 30 +/* GPRS PDP connection has MTU to 1500 */ #define SIZE_MTU 1500 -#define SIZE_MTU_MAX 4080 -#define SIZE_MTU_MIN 68 +/* 5 sec. connect timeout */ +#define CONNECT_TIMEOUT (5 * HZ) #define CAIF_NET_DEFAULT_QUEUE_LEN 500 #undef pr_debug @@ -37,6 +37,13 @@ static LIST_HEAD(chnl_net_list); MODULE_LICENSE("GPL"); MODULE_ALIAS_RTNL_LINK("caif"); +enum caif_states { + CAIF_CONNECTED = 1, + CAIF_CONNECTING, + CAIF_DISCONNECTED, + CAIF_SHUTDOWN +}; + struct chnl_net { struct cflayer chnl; struct net_device_stats stats; @@ -47,7 +54,7 @@ struct chnl_net { wait_queue_head_t netmgmt_wq; /* Flow status to remember and control the transmission. */ bool flowenabled; - bool pending_close; + enum caif_states state; }; static void robust_list_del(struct list_head *delete_node) @@ -58,15 +65,16 @@ static void robust_list_del(struct list_head *delete_node) list_for_each_safe(list_node, n, &chnl_net_list) { if (list_node == delete_node) { list_del(list_node); - break; + return; } } + WARN_ON(1); } static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) { struct sk_buff *skb; - struct chnl_net *priv = NULL; + struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); int pktlen; int err = 0; @@ -91,7 +99,6 @@ static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt) else skb->ip_summed = CHECKSUM_NONE; - /* FIXME: Drivers should call this in tasklet context. */ if (in_interrupt()) netif_rx(skb); else @@ -117,23 +124,25 @@ static void close_work(struct work_struct *work) struct chnl_net *dev = NULL; struct list_head *list_node; struct list_head *_tmp; - rtnl_lock(); + /* May be called with or without RTNL lock held */ + int islocked = rtnl_is_locked(); + if (!islocked) + rtnl_lock(); list_for_each_safe(list_node, _tmp, &chnl_net_list) { dev = list_entry(list_node, struct chnl_net, list_field); - if (!dev->pending_close) - continue; - list_del(list_node); - delete_device(dev); + if (dev->state == CAIF_SHUTDOWN) + dev_close(dev->netdev); } - rtnl_unlock(); + if (!islocked) + rtnl_unlock(); } static DECLARE_WORK(close_worker, close_work); static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, int phyid) { - struct chnl_net *priv; - pr_debug("CAIF: %s(): NET flowctrl func called flow: %s.\n", + struct chnl_net *priv = container_of(layr, struct chnl_net, chnl); + pr_debug("CAIF: %s(): NET flowctrl func called flow: %s\n", __func__, flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" : flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" : @@ -143,21 +152,31 @@ static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow, flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ? "REMOTE_SHUTDOWN" : "UKNOWN CTRL COMMAND"); - priv = container_of(layr, struct chnl_net, chnl); + switch (flow) { case CAIF_CTRLCMD_FLOW_OFF_IND: + priv->flowenabled = false; + netif_stop_queue(priv->netdev); + break; case CAIF_CTRLCMD_DEINIT_RSP: + priv->state = CAIF_DISCONNECTED; + break; case CAIF_CTRLCMD_INIT_FAIL_RSP: + priv->state = CAIF_DISCONNECTED; + wake_up_interruptible(&priv->netmgmt_wq); + break; case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: - priv->flowenabled = false; + priv->state = CAIF_SHUTDOWN; netif_tx_disable(priv->netdev); - pr_warning("CAIF: %s(): done\n", __func__); - priv->pending_close = 1; schedule_work(&close_worker); break; case CAIF_CTRLCMD_FLOW_ON_IND: + priv->flowenabled = true; + netif_wake_queue(priv->netdev); + break; case CAIF_CTRLCMD_INIT_RSP: + priv->state = CAIF_CONNECTED; priv->flowenabled = true; netif_wake_queue(priv->netdev); wake_up_interruptible(&priv->netmgmt_wq); @@ -194,9 +213,6 @@ static int chnl_net_start_xmit(struct sk_buff *skb, struct net_device *dev) pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb); - pr_debug("CAIF: %s(): transmit inst %s %d,%p\n", - __func__, dev->name, priv->chnl.dn->id, &priv->chnl.dn); - /* Send the packet down the stack. */ result = priv->chnl.dn->transmit(priv->chnl.dn, pkt); if (result) { @@ -217,61 +233,59 @@ static int chnl_net_open(struct net_device *dev) struct chnl_net *priv = NULL; int result = -1; ASSERT_RTNL(); - priv = netdev_priv(dev); - pr_debug("CAIF: %s(): dev name: %s\n", __func__, priv->name); - if (!priv) { pr_debug("CAIF: %s(): chnl_net_open: no priv\n", __func__); return -ENODEV; } - result = caif_connect_client(&priv->conn_req, &priv->chnl); - if (result != 0) { - pr_debug("CAIF: %s(): err: " - "Unable to register and open device, Err:%d\n", - __func__, - result); - return -ENODEV; + + if (priv->state != CAIF_CONNECTING) { + priv->state = CAIF_CONNECTING; + result = caif_connect_client(&priv->conn_req, &priv->chnl); + if (result != 0) { + priv->state = CAIF_DISCONNECTED; + pr_debug("CAIF: %s(): err: " + "Unable to register and open device," + " Err:%d\n", + __func__, + result); + return result; + } } - result = wait_event_interruptible(priv->netmgmt_wq, priv->flowenabled); + + result = wait_event_interruptible_timeout(priv->netmgmt_wq, + priv->state != CAIF_CONNECTING, + CONNECT_TIMEOUT); if (result == -ERESTARTSYS) { pr_debug("CAIF: %s(): wait_event_interruptible" " woken by a signal\n", __func__); return -ERESTARTSYS; - } else - pr_debug("CAIF: %s(): Flow on recieved\n", __func__); + } + if (result == 0) { + pr_debug("CAIF: %s(): connect timeout\n", __func__); + caif_disconnect_client(&priv->chnl); + priv->state = CAIF_DISCONNECTED; + pr_debug("CAIF: %s(): state disconnected\n", __func__); + return -ETIMEDOUT; + } + if (priv->state != CAIF_CONNECTED) { + pr_debug("CAIF: %s(): connect failed\n", __func__); + return -ECONNREFUSED; + } + pr_debug("CAIF: %s(): CAIF Netdevice connected\n", __func__); return 0; } static int chnl_net_stop(struct net_device *dev) { struct chnl_net *priv; - int result = -1; + ASSERT_RTNL(); priv = netdev_priv(dev); - - result = caif_disconnect_client(&priv->chnl); - if (result != 0) { - pr_debug("CAIF: %s(): chnl_net_stop: err: " - "Unable to STOP device, Err:%d\n", - __func__, result); - return -EBUSY; - } - result = wait_event_interruptible(priv->netmgmt_wq, - !priv->flowenabled); - - if (result == -ERESTARTSYS) { - pr_debug("CAIF: %s(): wait_event_interruptible woken by" - " signal, signal_pending(current) = %d\n", - __func__, - signal_pending(current)); - } else { - pr_debug("CAIF: %s(): disconnect received\n", __func__); - - } - + priv->state = CAIF_DISCONNECTED; + caif_disconnect_client(&priv->chnl); return 0; } @@ -377,6 +391,8 @@ static int ipcaif_newlink(struct net *src_net, struct net_device *dev, ASSERT_RTNL(); caifdev = netdev_priv(dev); caif_netlink_parms(data, &caifdev->conn_req); + dev_net_set(caifdev->netdev, src_net); + ret = register_netdevice(dev); if (ret) pr_warning("CAIF: %s(): device rtml registration failed\n", |