]> Pileus Git - ~andy/linux/blobdiff - net/caif/cfmuxl.c
Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[~andy/linux] / net / caif / cfmuxl.c
index 2a56df7e0a4b05539fba6427f1ceb6672ee4d4d0..3a66b8c10e09ab1876e7b678efc65073699342a0 100644 (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();
 }