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>
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 @@
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 @@
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 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 @@
{
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();
}