Commit 62305823 authored by Stefan Richter's avatar Stefan Richter

firewire: core: fix sleep in atomic context due to driver core change

Due to commit 2831fe6f, "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: default avatarJay Fenlason <fenlason@redhat.com>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 73d59314
...@@ -209,6 +209,8 @@ fw_card_bm_work(struct work_struct *work) ...@@ -209,6 +209,8 @@ fw_card_bm_work(struct work_struct *work)
unsigned long flags; unsigned long flags;
int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode; int root_id, new_root_id, irm_id, gap_count, generation, grace, rcode;
bool do_reset = false; bool do_reset = false;
bool root_device_is_running;
bool root_device_is_cmc;
__be32 lock_data[2]; __be32 lock_data[2];
spin_lock_irqsave(&card->lock, flags); spin_lock_irqsave(&card->lock, flags);
...@@ -224,8 +226,9 @@ fw_card_bm_work(struct work_struct *work) ...@@ -224,8 +226,9 @@ fw_card_bm_work(struct work_struct *work)
generation = card->generation; generation = card->generation;
root_device = root_node->data; root_device = root_node->data;
if (root_device) root_device_is_running = root_device &&
fw_device_get(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; root_id = root_node->node_id;
grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10));
...@@ -308,14 +311,14 @@ fw_card_bm_work(struct work_struct *work) ...@@ -308,14 +311,14 @@ fw_card_bm_work(struct work_struct *work)
* config rom. In either case, pick another root. * config rom. In either case, pick another root.
*/ */
new_root_id = local_node->node_id; 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 * If we haven't probed this device yet, bail out now
* and let's try again once that's done. * and let's try again once that's done.
*/ */
spin_unlock_irqrestore(&card->lock, flags); spin_unlock_irqrestore(&card->lock, flags);
goto out; goto out;
} else if (root_device->cmc) { } else if (root_device_is_cmc) {
/* /*
* FIXME: I suppose we should set the cmstr bit in the * FIXME: I suppose we should set the cmstr bit in the
* STATE_CLEAR register of this node, as described in * STATE_CLEAR register of this node, as described in
...@@ -362,8 +365,6 @@ fw_card_bm_work(struct work_struct *work) ...@@ -362,8 +365,6 @@ fw_card_bm_work(struct work_struct *work)
fw_core_initiate_bus_reset(card, 1); fw_core_initiate_bus_reset(card, 1);
} }
out: out:
if (root_device)
fw_device_put(root_device);
fw_node_put(root_node); fw_node_put(root_node);
fw_node_put(local_node); fw_node_put(local_node);
out_put_card: out_put_card:
......
...@@ -159,7 +159,8 @@ static void fw_device_release(struct device *dev) ...@@ -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 * 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); spin_lock_irqsave(&card->lock, flags);
device->node->data = NULL; device->node->data = NULL;
...@@ -695,12 +696,13 @@ static void fw_device_init(struct work_struct *work) ...@@ -695,12 +696,13 @@ static void fw_device_init(struct work_struct *work)
return; return;
} }
err = -ENOMEM; device_initialize(&device->device);
fw_device_get(device); fw_device_get(device);
down_write(&fw_device_rwsem); down_write(&fw_device_rwsem);
if (idr_pre_get(&fw_device_idr, GFP_KERNEL)) err = idr_pre_get(&fw_device_idr, GFP_KERNEL) ?
err = idr_get_new(&fw_device_idr, device, &minor); idr_get_new(&fw_device_idr, device, &minor) :
-ENOMEM;
up_write(&fw_device_rwsem); up_write(&fw_device_rwsem);
if (err < 0) if (err < 0)
...@@ -911,13 +913,14 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) ...@@ -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 * Do minimal intialization of the device here, the
* rest will happen in fw_device_init(). We need the * rest will happen in fw_device_init().
* card and node so we can read the config rom and we *
* need to do device_initialize() now so * Attention: A lot of things, even fw_device_get(),
* device_for_each_child() in FW_NODE_UPDATED is * cannot be done before fw_device_init() finished!
* doesn't freak out. * 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); atomic_set(&device->state, FW_DEVICE_INITIALIZING);
device->card = fw_card_get(card); device->card = fw_card_get(card);
device->node = fw_node_get(node); device->node = fw_node_get(node);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment