In some cases, we only want to wait for a specific cookie rather than all the cookies smaller than it. Introduces two new interfaces async_synchronize_one_cookie async_synchronize_one_cookie_special users can use these to wait for a specific cookie. For example: device cookie ACPI battery 1 ata port 0 2 ata port 1 3 ata port 0 and port 1 in the same host can not be porbed in parallel. In this case, ata port1 should only wait for cookie 2 rather than both 1 and 2. And using the new interface would be a better choice. Signed-off-by: Zhang Rui --- drivers/ata/libata-core.c | 4 +- include/linux/async.h | 6 ++- kernel/async.c | 82 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 12 deletions(-) Index: linux-2.6/kernel/async.c =================================================================== --- linux-2.6.orig/kernel/async.c +++ linux-2.6/kernel/async.c @@ -87,6 +87,44 @@ extern int initcall_debug; /* * MUST be called with the lock held! */ +static int __cookie_is_done(struct list_head *running, async_cookie_t cookie) +{ + struct async_entry *entry; + + if (!list_empty(running)) { + list_for_each_entry(entry, running, list) { + if (entry->cookie > cookie) + break; + if (entry->cookie == cookie) + return 0; + } + } + + if (!list_empty(&async_pending)) { + list_for_each_entry(entry, &async_pending, list) { + if (entry->cookie > cookie) + break; + if (entry->cookie == cookie) + return 0; + } + } + return 1; +} + +static int cookie_is_done(struct list_head *running, async_cookie_t cookie) +{ + unsigned long flags; + async_cookie_t ret; + + spin_lock_irqsave(&async_lock, flags); + ret = __cookie_is_done(running, cookie); + spin_unlock_irqrestore(&async_lock, flags); + return ret; +} + +/* + * MUST be called with the lock held! + */ static async_cookie_t __lowest_in_progress(struct list_head *running) { struct async_entry *entry; @@ -220,18 +258,19 @@ EXPORT_SYMBOL_GPL(async_schedule_special void async_synchronize_full(void) { do { - async_synchronize_cookie(next_cookie); + async_synchronize_cookies(next_cookie); } while (!list_empty(&async_running) || !list_empty(&async_pending)); } EXPORT_SYMBOL_GPL(async_synchronize_full); void async_synchronize_full_special(struct list_head *list) { - async_synchronize_cookie_special(next_cookie, list); + async_synchronize_cookies_special(next_cookie, list); } EXPORT_SYMBOL_GPL(async_synchronize_full_special); -void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *running) +static void __async_synchronize_cookie_special(async_cookie_t cookie, + struct list_head *running, int type) { ktime_t starttime, delta, endtime; @@ -240,7 +279,10 @@ void async_synchronize_cookie_special(as starttime = ktime_get(); } - wait_event(async_done, lowest_in_progress(running) >= cookie); + if (type) + wait_event(async_done, lowest_in_progress(running) >= cookie); + else + wait_event(async_done, cookie_is_done(running, cookie)); if (initcall_debug && system_state == SYSTEM_BOOTING) { endtime = ktime_get(); @@ -250,13 +292,37 @@ void async_synchronize_cookie_special(as task_pid_nr(current), ktime_to_ns(delta) >> 10); } } -EXPORT_SYMBOL_GPL(async_synchronize_cookie_special); -void async_synchronize_cookie(async_cookie_t cookie) +void async_synchronize_cookies_special(async_cookie_t cookie, struct list_head *running) +{ + __async_synchronize_cookie_special(cookie, running, 1); +} +EXPORT_SYMBOL_GPL(async_synchronize_cookies_special); + +void async_synchronize_cookies(async_cookie_t cookie) +{ + __async_synchronize_cookie_special(cookie, &async_running, 1); +} +EXPORT_SYMBOL_GPL(async_synchronize_cookies); + + +/* + * sychronize a specific cookie + * block until the entry with a speicific cookie is done. + * Note that waiting for a cookie while holding it is not allowed. + * because it may cause a deadlock issue. + */ +void async_synchronize_one_cookie_special(async_cookie_t cookie, struct list_head *running) +{ + __async_synchronize_cookie_special(cookie, running, 0); +} +EXPORT_SYMBOL_GPL(async_synchronize_one_cookie_special); + +void async_synchronize_one_cookie(async_cookie_t cookie) { - async_synchronize_cookie_special(cookie, &async_running); + __async_synchronize_cookie_special(cookie, &async_running, 0); } -EXPORT_SYMBOL_GPL(async_synchronize_cookie); +EXPORT_SYMBOL_GPL(async_synchronize_one_cookie); static int async_thread(void *unused) Index: linux-2.6/include/linux/async.h =================================================================== --- linux-2.6.orig/include/linux/async.h +++ linux-2.6/include/linux/async.h @@ -20,6 +20,8 @@ extern async_cookie_t async_schedule(asy extern async_cookie_t async_schedule_special(async_func_ptr *ptr, void *data, struct list_head *list); extern void async_synchronize_full(void); extern void async_synchronize_full_special(struct list_head *list); -extern void async_synchronize_cookie(async_cookie_t cookie); -extern void async_synchronize_cookie_special(async_cookie_t cookie, struct list_head *list); +extern void async_synchronize_cookies(async_cookie_t cookie); +extern void async_synchronize_cookies_special(async_cookie_t cookie, struct list_head *list); +extern void async_synchronize_one_cookie(async_cookie_t cookie); +extern void async_synchronize_one_cookie_special(async_cookie_t cookie, struct list_head *list); Index: linux-2.6/drivers/ata/libata-core.c =================================================================== --- linux-2.6.orig/drivers/ata/libata-core.c +++ linux-2.6/drivers/ata/libata-core.c @@ -5929,7 +5929,7 @@ static void async_port_probe(void *data, * don't need to wait for port 0, only for later ports. */ if (!(ap->host->flags & ATA_HOST_PARALLEL_SCAN) && ap->port_no != 0) - async_synchronize_cookie(cookie); + async_synchronize_cookies(cookie); /* probe */ if (ap->ops->error_handler) { @@ -5969,7 +5969,7 @@ static void async_port_probe(void *data, } /* in order to keep device order, we need to synchronize at this point */ - async_synchronize_cookie(cookie); + async_synchronize_cookies(cookie); ata_scsi_scan_host(ap, 1);