summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorAndrew Morton <akpm@osdl.org>2006-10-27 11:42:37 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-27 15:34:51 -0700
commit735a7ffb739b6efeaeb1e720306ba308eaaeb20e (patch)
tree6156c96aeae04e1fd789f07bdd839dca7eca611a /drivers/base
parent61ce1efe6e40233663d27ab8ac9ba9710eebcaad (diff)
downloadlinux-735a7ffb739b6efeaeb1e720306ba308eaaeb20e.tar.bz2
[PATCH] drivers: wait for threaded probes between initcall levels
The multithreaded-probing code has a problem: after one initcall level (eg, core_initcall) has been processed, we will then start processing the next level (postcore_initcall) while the kernel threads which are handling core_initcall are still executing. This breaks the guarantees which the layered initcalls previously gave us. IOW, we want to be multithreaded _within_ an initcall level, but not between different levels. Fix that up by causing the probing code to wait for all outstanding probes at one level to complete before we start processing the next level. Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/dd.c30
1 files changed, 30 insertions, 0 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index db01b95a47a5..c5d6bb4290ad 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kthread.h>
+#include <linux/wait.h>
#include "base.h"
#include "power/power.h"
@@ -70,6 +71,8 @@ struct stupid_thread_structure {
};
static atomic_t probe_count = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue);
+
static int really_probe(void *void_data)
{
struct stupid_thread_structure *data = void_data;
@@ -121,6 +124,7 @@ probe_failed:
done:
kfree(data);
atomic_dec(&probe_count);
+ wake_up(&probe_waitqueue);
return ret;
}
@@ -337,6 +341,32 @@ void driver_detach(struct device_driver * drv)
}
}
+#ifdef CONFIG_PCI_MULTITHREAD_PROBE
+static int __init wait_for_probes(void)
+{
+ DEFINE_WAIT(wait);
+
+ printk(KERN_INFO "%s: waiting for %d threads\n", __FUNCTION__,
+ atomic_read(&probe_count));
+ if (!atomic_read(&probe_count))
+ return 0;
+ while (atomic_read(&probe_count)) {
+ prepare_to_wait(&probe_waitqueue, &wait, TASK_UNINTERRUPTIBLE);
+ if (atomic_read(&probe_count))
+ schedule();
+ }
+ finish_wait(&probe_waitqueue, &wait);
+ return 0;
+}
+
+core_initcall_sync(wait_for_probes);
+postcore_initcall_sync(wait_for_probes);
+arch_initcall_sync(wait_for_probes);
+subsys_initcall_sync(wait_for_probes);
+fs_initcall_sync(wait_for_probes);
+device_initcall_sync(wait_for_probes);
+late_initcall_sync(wait_for_probes);
+#endif
EXPORT_SYMBOL_GPL(device_bind_driver);
EXPORT_SYMBOL_GPL(device_release_driver);