firewire: core: fix sleep in atomic context due to driver core change
authorStefan Richter <stefanr@s5r6.in-berlin.de>
Fri, 9 Jan 2009 19:49:37 +0000 (20:49 +0100)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Fri, 9 Jan 2009 22:22:32 +0000 (23:22 +0100)
Due to commit 2831fe6f9cc4e16c103504ee09a47a084297c0f3, "driver core:
create a private portion of struct device", device_initialize() can no
longer be called from atomic contexts.

We now defer it until after config ROM probing.  This requires changes
to the bus manager code because this may use a device before it was
probed.

Reported-by: Jay Fenlason <fenlason@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/fw-card.c
drivers/firewire/fw-device.c

index 799f944..6bd91a1 100644 (file)
@@ -209,6 +209,8 @@ fw_card_bm_work(struct work_struct *work)
        unsigned long flags;
        int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
        bool do_reset = false;
+       bool root_device_is_running;
+       bool root_device_is_cmc;
        __be32 lock_data[2];
 
        spin_lock_irqsave(&card->lock, flags);
@@ -224,8 +226,9 @@ fw_card_bm_work(struct work_struct *work)
 
        generation = card->generation;
        root_device = root_node->data;
-       if (root_device)
-               fw_device_get(root_device);
+       root_device_is_running = root_device &&
+                       atomic_read(&root_device->state) == FW_DEVICE_RUNNING;
+       root_device_is_cmc = root_device && root_device->cmc;
        root_id = root_node->node_id;
        grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
 
@@ -308,14 +311,14 @@ fw_card_bm_work(struct work_struct *work)
                 * config rom.  In either case, pick another root.
                 */
                new_root_id = local_node->node_id;
-       } else if (atomic_read(&root_device->state) != FW_DEVICE_RUNNING) {
+       } else if (!root_device_is_running) {
                /*
                 * If we haven't probed this device yet, bail out now
                 * and let's try again once that's done.
                 */
                spin_unlock_irqrestore(&card->lock, flags);
                goto out;
-       } else if (root_device->cmc) {
+       } else if (root_device_is_cmc) {
                /*
                 * FIXME: I suppose we should set the cmstr bit in the
                 * STATE_CLEAR register of this node, as described in
@@ -362,8 +365,6 @@ fw_card_bm_work(struct work_struct *work)
                fw_core_initiate_bus_reset(card, 1);
        }
  out:
-       if (root_device)
-               fw_device_put(root_device);
        fw_node_put(root_node);
        fw_node_put(local_node);
  out_put_card:
index c173be3..2af5a8d 100644 (file)
@@ -159,7 +159,8 @@ static void fw_device_release(struct device *dev)
 
        /*
         * Take the card lock so we don't set this to NULL while a
-        * FW_NODE_UPDATED callback is being handled.
+        * FW_NODE_UPDATED callback is being handled or while the
+        * bus manager work looks at this node.
         */
        spin_lock_irqsave(&card->lock, flags);
        device->node->data = NULL;
@@ -695,12 +696,13 @@ static void fw_device_init(struct work_struct *work)
                return;
        }
 
-       err = -ENOMEM;
+       device_initialize(&device->device);
 
        fw_device_get(device);
        down_write(&fw_device_rwsem);
-       if (idr_pre_get(&fw_device_idr, GFP_KERNEL))
-               err = idr_get_new(&fw_device_idr, device, &minor);
+       err = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
+             idr_get_new(&fw_device_idr, device, &minor) :
+             -ENOMEM;
        up_write(&fw_device_rwsem);
 
        if (err < 0)
@@ -911,13 +913,14 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event)
 
                /*
                 * Do minimal intialization of the device here, the
-                * rest will happen in fw_device_init().  We need the
-                * card and node so we can read the config rom and we
-                * need to do device_initialize() now so
-                * device_for_each_child() in FW_NODE_UPDATED is
-                * doesn't freak out.
+                * rest will happen in fw_device_init().
+                *
+                * Attention:  A lot of things, even fw_device_get(),
+                * cannot be done before fw_device_init() finished!
+                * You can basically just check device->state and
+                * schedule work until then, but only while holding
+                * card->lock.
                 */
-               device_initialize(&device->device);
                atomic_set(&device->state, FW_DEVICE_INITIALIZING);
                device->card = fw_card_get(card);
                device->node = fw_node_get(node);