Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54e90fb5 authored by sjur.brandeland@stericsson.com's avatar sjur.brandeland@stericsson.com Committed by David S. Miller
Browse files

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: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0e5a1174
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -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 */
+19 −25
Original line number Diff line number Diff line
@@ -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);

	cfctrl_cancel_req(cfg->ctrl, adap_layer);
	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;
	}

	if (channel_id != 0) {
		struct cflayer *servl;
		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);
		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) {
+38 −11
Original line number Diff line number Diff line
@@ -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();
}