Skip to content

Commit

Permalink
caif: Fixes freeze on Link layer removal.
Browse files Browse the repository at this point in the history
CAIF Socket layer - caif_socket.c:
- Plug mem-leak at reconnect.
- Always call disconnect to cleanup CAIF stack.
- Disconnect will always report success.

CAIF configuration layer - cfcnfg.c
- Disconnect must dismantle the caif stack correctly
- Protect against faulty removals (check on id zero)

CAIF mux layer - cfmuxl.c
- When inserting new service layer in the MUX remove
  any old entries with the same ID.
- When removing CAIF Link layer, remove the associated
  service layers before notifying service layers.

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
sjur.brandeland@stericsson.com authored and David S. Miller committed May 23, 2011
1 parent 0e5a117 commit 54e90fb
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 44 deletions.
13 changes: 5 additions & 8 deletions net/caif/caif_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/caif/caif_socket.h>
#include <asm/atomic.h>
#include <linux/atomic.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <net/caif/caif_layer.h>
Expand Down Expand Up @@ -816,6 +816,7 @@ static int caif_connect(struct socket *sock, struct sockaddr *uaddr,
if (sk->sk_shutdown & SHUTDOWN_MASK) {
/* Allow re-connect after SHUTDOWN_IND */
caif_disconnect_client(sock_net(sk), &cf_sk->layer);
caif_free_client(&cf_sk->layer);
break;
}
/* No reconnect on a seqpacket socket */
Expand Down Expand Up @@ -926,7 +927,6 @@ static int caif_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
int res = 0;

if (!sk)
return 0;
Expand All @@ -953,18 +953,15 @@ static int caif_release(struct socket *sock)
sk->sk_state = CAIF_DISCONNECTED;
sk->sk_shutdown = SHUTDOWN_MASK;

if (cf_sk->sk.sk_socket->state == SS_CONNECTED ||
cf_sk->sk.sk_socket->state == SS_CONNECTING)
res = caif_disconnect_client(sock_net(sk), &cf_sk->layer);

caif_disconnect_client(sock_net(sk), &cf_sk->layer);
cf_sk->sk.sk_socket->state = SS_DISCONNECTING;
wake_up_interruptible_poll(sk_sleep(sk), POLLERR|POLLHUP);

sock_orphan(sk);
sk_stream_kill_queues(&cf_sk->sk);
release_sock(sk);
sock_put(sk);
return res;
return 0;
}

/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */
Expand Down Expand Up @@ -1120,7 +1117,7 @@ static int caif_create(struct net *net, struct socket *sock, int protocol,
set_rx_flow_on(cf_sk);

/* Set default options on configuration */
cf_sk->sk.sk_priority= CAIF_PRIO_NORMAL;
cf_sk->sk.sk_priority = CAIF_PRIO_NORMAL;
cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;
cf_sk->conn_req.protocol = protocol;
/* Increase the number of sockets created. */
Expand Down
44 changes: 19 additions & 25 deletions net/caif/cfcnfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,39 +182,26 @@ static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)

int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
{
u8 channel_id = 0;
int ret = 0;
struct cflayer *servl = NULL;
u8 channel_id;
struct cfcnfg *cfg = get_cfcnfg(net);

caif_assert(adap_layer != NULL);

channel_id = adap_layer->id;
if (adap_layer->dn == NULL || channel_id == 0) {
pr_err("adap_layer->dn == NULL or adap_layer->id is 0\n");
ret = -ENOTCONN;
goto end;
}

servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
if (servl == NULL) {
pr_err("PROTOCOL ERROR - "
"Error removing service_layer Channel_Id(%d)",
channel_id);
ret = -EINVAL;
goto end;
}

ret = cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);

end:
cfctrl_cancel_req(cfg->ctrl, adap_layer);
channel_id = adap_layer->id;
if (channel_id != 0) {
struct cflayer *servl;
servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
if (servl != NULL)
layer_set_up(servl, NULL);
} else
pr_debug("nothing to disconnect\n");
cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);

/* Do RCU sync before initiating cleanup */
synchronize_rcu();
if (adap_layer->ctrlcmd != NULL)
adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
return ret;
return 0;

}
EXPORT_SYMBOL(caif_disconnect_client);
Expand Down Expand Up @@ -400,6 +387,14 @@ cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
struct cfcnfg_phyinfo *phyinfo;
struct net_device *netdev;

if (channel_id == 0) {
pr_warn("received channel_id zero\n");
if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
adapt_layer->ctrlcmd(adapt_layer,
CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
return;
}

rcu_read_lock();

if (adapt_layer == NULL) {
Expand Down Expand Up @@ -523,7 +518,6 @@ cfcnfg_add_phy_layer(struct cfcnfg *cnfg, enum cfcnfg_phy_type phy_type,
phyinfo->use_stx = stx;
phyinfo->use_fcs = fcs;

phy_layer->type = phy_type;
frml = cffrml_create(phyid, fcs);

if (!frml) {
Expand Down
49 changes: 38 additions & 11 deletions net/caif/cfmuxl.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,6 @@ struct cflayer *cfmuxl_create(void)
return &this->layer;
}

int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
{
struct cfmuxl *muxl = container_obj(layr);

spin_lock_bh(&muxl->receive_lock);
list_add_rcu(&up->node, &muxl->srvl_list);
spin_unlock_bh(&muxl->receive_lock);
return 0;
}

int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
{
struct cfmuxl *muxl = (struct cfmuxl *) layr;
Expand All @@ -93,6 +83,24 @@ static struct cflayer *get_from_id(struct list_head *list, u16 id)
return NULL;
}

int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *old;

spin_lock_bh(&muxl->receive_lock);

/* Two entries with same id is wrong, so remove old layer from mux */
old = get_from_id(&muxl->srvl_list, linkid);
if (old != NULL)
list_del_rcu(&old->node);

list_add_rcu(&up->node, &muxl->srvl_list);
spin_unlock_bh(&muxl->receive_lock);

return 0;
}

struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
{
struct cfmuxl *muxl = container_obj(layr);
Expand Down Expand Up @@ -146,6 +154,11 @@ struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
struct cfmuxl *muxl = container_obj(layr);
int idx = id % UP_CACHE_SIZE;

if (id == 0) {
pr_warn("Trying to remove control layer\n");
return NULL;
}

spin_lock_bh(&muxl->receive_lock);
up = get_from_id(&muxl->srvl_list, id);
if (up == NULL)
Expand Down Expand Up @@ -235,12 +248,26 @@ static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
{
struct cfmuxl *muxl = container_obj(layr);
struct cflayer *layer;
int idx;

rcu_read_lock();
list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd)

if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {

if ((ctrl == _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND ||
ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
layer->id != 0) {

idx = layer->id % UP_CACHE_SIZE;
spin_lock_bh(&muxl->receive_lock);
rcu_assign_pointer(muxl->up_cache[idx], NULL);
list_del_rcu(&layer->node);
spin_unlock_bh(&muxl->receive_lock);
}
/* NOTE: ctrlcmd is not allowed to block */
layer->ctrlcmd(layer, ctrl, phyid);
}
}
rcu_read_unlock();
}

0 comments on commit 54e90fb

Please sign in to comment.