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:
sjur.brandeland@stericsson.com
2011-05-22 11:18:51 +00:00
committed by David S. Miller
parent 0e5a117441
commit 54e90fb5ca
3 changed files with 62 additions and 44 deletions
+38 -11
View File
@@ -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();
}