mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
caif: Fixes freeze on Link layer removal.
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>
This commit is contained in:
parent
0e5a117441
commit
54e90fb5ca
@ -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>
|
||||
@ -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 */
|
||||
@ -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;
|
||||
@ -953,10 +953,7 @@ 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);
|
||||
|
||||
@ -964,7 +961,7 @@ static int caif_release(struct socket *sock)
|
||||
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 */
|
||||
@ -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. */
|
||||
|
@ -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);
|
||||
@ -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) {
|
||||
@ -523,7 +518,6 @@ got_phyid:
|
||||
phyinfo->use_stx = stx;
|
||||
phyinfo->use_fcs = fcs;
|
||||
|
||||
phy_layer->type = phy_type;
|
||||
frml = cffrml_create(phyid, fcs);
|
||||
|
||||
if (!frml) {
|
||||
|
@ -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;
|
||||
@ -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);
|
||||
@ -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)
|
||||
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user