Revert "drm: don't associate _DRM_DRIVER maps with a master"
[safe/jmp/linux-2.6] / drivers / firewire / fw-topology.c
index 45e6f9b..d0deecc 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/module.h>
 #include <linux/wait.h>
 #include <linux/errno.h>
+#include <asm/bug.h>
+#include <asm/system.h>
 #include "fw-transaction.h"
 #include "fw-topology.h"
 
@@ -106,6 +108,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color)
        node->node_id = LOCAL_BUS | SELF_ID_PHY_ID(sid);
        node->link_on = SELF_ID_LINK_ON(sid);
        node->phy_speed = SELF_ID_PHY_SPEED(sid);
+       node->initiated_reset = SELF_ID_PHY_INITIATOR(sid);
        node->port_count = port_count;
 
        atomic_set(&node->ref_count, 1);
@@ -166,7 +169,7 @@ static inline struct fw_node *fw_node(struct list_head *l)
  * This function builds the tree representation of the topology given
  * by the self IDs from the latest bus reset.  During the construction
  * of the tree, the function checks that the self IDs are valid and
- * internally consistent.  On succcess this funtions returns the
+ * internally consistent.  On succcess this function returns the
  * fw_node corresponding to the local card otherwise NULL.
  */
 static struct fw_node *build_tree(struct fw_card *card,
@@ -215,6 +218,10 @@ static struct fw_node *build_tree(struct fw_card *card,
                 */
                for (i = 0, h = &stack; i < child_port_count; i++)
                        h = h->prev;
+               /*
+                * When the stack is empty, this yields an invalid value,
+                * but that pointer will never be dereferenced.
+                */
                child = fw_node(h);
 
                node = fw_node_create(q, port_count, card->color);
@@ -283,12 +290,11 @@ static struct fw_node *build_tree(struct fw_card *card,
                        beta_repeaters_present = true;
 
                /*
-                * If all PHYs does not report the same gap count
-                * setting, we fall back to 63 which will force a gap
-                * count reconfiguration and a reset.
+                * If PHYs report different gap counts, set an invalid count
+                * which will force a gap count reconfiguration and a reset.
                 */
                if (SELF_ID_GAP_COUNT(q) != gap_count)
-                       gap_count = 63;
+                       gap_count = 0;
 
                update_hop_count(node);
 
@@ -308,9 +314,8 @@ typedef void (*fw_node_callback_t)(struct fw_card * card,
                                   struct fw_node * node,
                                   struct fw_node * parent);
 
-static void
-for_each_fw_node(struct fw_card *card, struct fw_node *root,
-                fw_node_callback_t callback)
+static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
+                            fw_node_callback_t callback)
 {
        struct list_head list;
        struct fw_node *node, *next, *child, *parent;
@@ -343,17 +348,18 @@ for_each_fw_node(struct fw_card *card, struct fw_node *root,
                fw_node_put(node);
 }
 
-static void
-report_lost_node(struct fw_card *card,
-                struct fw_node *node, struct fw_node *parent)
+static void report_lost_node(struct fw_card *card,
+                            struct fw_node *node, struct fw_node *parent)
 {
        fw_node_event(card, node, FW_NODE_DESTROYED);
        fw_node_put(node);
+
+       /* Topology has changed - reset bus manager retry counter */
+       card->bm_retries = 0;
 }
 
-static void
-report_found_node(struct fw_card *card,
-                 struct fw_node *node, struct fw_node *parent)
+static void report_found_node(struct fw_card *card,
+                             struct fw_node *node, struct fw_node *parent)
 {
        int b_path = (node->phy_speed == SCODE_BETA);
 
@@ -368,6 +374,9 @@ report_found_node(struct fw_card *card,
        }
 
        fw_node_event(card, node, FW_NODE_CREATED);
+
+       /* Topology has changed - reset bus manager retry counter */
+       card->bm_retries = 0;
 }
 
 void fw_destroy_nodes(struct fw_card *card)
@@ -378,6 +387,7 @@ void fw_destroy_nodes(struct fw_card *card)
        card->color++;
        if (card->local_node != NULL)
                for_each_fw_node(card, card->local_node, report_lost_node);
+       card->local_node = NULL;
        spin_unlock_irqrestore(&card->lock, flags);
 }
 
@@ -402,11 +412,10 @@ static void move_tree(struct fw_node *node0, struct fw_node *node1, int port)
  * found, lost or updated.  Update the nodes in the card topology tree
  * as we go.
  */
-static void
-update_tree(struct fw_card *card, struct fw_node *root)
+static void update_tree(struct fw_card *card, struct fw_node *root)
 {
        struct list_head list0, list1;
-       struct fw_node *node0, *node1;
+       struct fw_node *node0, *node1, *next1;
        int i, event;
 
        INIT_LIST_HEAD(&list0);
@@ -418,12 +427,14 @@ update_tree(struct fw_card *card, struct fw_node *root)
        node1 = fw_node(list1.next);
 
        while (&node0->link != &list0) {
+               WARN_ON(node0->port_count != node1->port_count);
 
-               /* assert(node0->port_count == node1->port_count); */
                if (node0->link_on && !node1->link_on)
                        event = FW_NODE_LINK_OFF;
                else if (!node0->link_on && node1->link_on)
                        event = FW_NODE_LINK_ON;
+               else if (node1->initiated_reset && node1->link_on)
+                       event = FW_NODE_INITIATED_RESET;
                else
                        event = FW_NODE_UPDATED;
 
@@ -476,12 +487,14 @@ update_tree(struct fw_card *card, struct fw_node *root)
                }
 
                node0 = fw_node(node0->link.next);
-               node1 = fw_node(node1->link.next);
+               next1 = fw_node(node1->link.next);
+               fw_node_put(node1);
+               node1 = next1;
        }
 }
 
-static void
-update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
+static void update_topology_map(struct fw_card *card,
+                               u32 *self_ids, int self_id_count)
 {
        int node_count;
 
@@ -493,30 +506,36 @@ update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
        fw_compute_block_crc(card->topology_map);
 }
 
-void
-fw_core_handle_bus_reset(struct fw_card *card,
-                        int node_id, int generation,
-                        int self_id_count, u32 * self_ids)
+void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
+                             int self_id_count, u32 *self_ids)
 {
        struct fw_node *local_node;
        unsigned long flags;
 
-       fw_flush_transactions(card);
-
-       spin_lock_irqsave(&card->lock, flags);
-
        /*
-        * If the new topology has a different self_id_count the topology
-        * changed, either nodes were added or removed. In that case we
-        * reset the IRM reset counter.
+        * If the selfID buffer is not the immediate successor of the
+        * previously processed one, we cannot reliably compare the
+        * old and new topologies.
         */
-       if (card->self_id_count != self_id_count)
+       if (!is_next_generation(generation, card->generation) &&
+           card->local_node != NULL) {
+               fw_notify("skipped bus generations, destroying all nodes\n");
+               fw_destroy_nodes(card);
                card->bm_retries = 0;
+       }
+
+       spin_lock_irqsave(&card->lock, flags);
 
+       card->broadcast_channel_allocated = false;
        card->node_id = node_id;
+       /*
+        * Update node_id before generation to prevent anybody from using
+        * a stale node_id together with a current generation.
+        */
+       smp_wmb();
        card->generation = generation;
        card->reset_jiffies = jiffies;
-       schedule_delayed_work(&card->work, 0);
+       fw_schedule_bm_work(card, 0);
 
        local_node = build_tree(card, self_ids, self_id_count);