Patchwork [2/5] caif: Fixes freeze on Link layer removal.

login
register
mail settings
Submitter sjur.brandeland@stericsson.com
Date May 22, 2011, 9:18 p.m.
Message ID <1306099134-12989-2-git-send-email-sjur.brandeland@stericsson.com>
Download mbox | patch
Permalink /patch/96787/
State Accepted
Delegated to: David Miller
Headers show

Comments

sjur.brandeland@stericsson.com - May 22, 2011, 9:18 p.m.
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>
---
 net/caif/caif_socket.c |   13 ++++-------
 net/caif/cfcnfg.c      |   44 ++++++++++++++++++------------------------
 net/caif/cfmuxl.c      |   49 +++++++++++++++++++++++++++++++++++++----------
 3 files changed, 62 insertions(+), 44 deletions(-)
David Miller - May 23, 2011, 12:13 a.m.
From: Sjur Brændeland <sjur.brandeland@stericsson.com>
Date: Sun, 22 May 2011 23:18:51 +0200

> 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>

Applied.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index b840395..a986280 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -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. */
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
index 351c2ca..52fe33b 100644
--- a/net/caif/cfcnfg.c
+++ b/net/caif/cfcnfg.c
@@ -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) {
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index 2a56df7..3a66b8c 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -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();
 }