Implement work/async_current_func() which query whether the current task is a workqueue or async worker respectively and, if so, return the current function being executed along with work / async item related information. This will be used to implement warning on synchronous request_module() from async workers. Signed-off-by: Tejun Heo <tj@xxxxxxxxxx> Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx> Cc: Arjan van de Ven <arjan@xxxxxxxxxxxxxxx> --- include/linux/async.h | 2 ++ include/linux/workqueue.h | 1 + kernel/async.c | 25 +++++++++++++++++++++++++ kernel/workqueue.c | 22 ++++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/include/linux/async.h b/include/linux/async.h index 7a24fe9..6c49157 100644 --- a/include/linux/async.h +++ b/include/linux/async.h @@ -52,4 +52,6 @@ extern void async_synchronize_full_domain(struct async_domain *domain); extern void async_synchronize_cookie(async_cookie_t cookie); extern void async_synchronize_cookie_domain(async_cookie_t cookie, struct async_domain *domain); +extern async_func_ptr *async_current_func(void **datap, + async_cookie_t *cookiep); #endif diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 2b58905..984fbef 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -428,6 +428,7 @@ extern void workqueue_set_max_active(struct workqueue_struct *wq, extern bool workqueue_congested(unsigned int cpu, struct workqueue_struct *wq); extern unsigned int work_cpu(struct work_struct *work); extern unsigned int work_busy(struct work_struct *work); +extern work_func_t work_current_func(struct work_struct **workp); /* * Like above, but uses del_timer() instead of del_timer_sync(). This means, diff --git a/kernel/async.c b/kernel/async.c index 9d31183..ed1eda0 100644 --- a/kernel/async.c +++ b/kernel/async.c @@ -337,3 +337,28 @@ void async_synchronize_cookie(async_cookie_t cookie) async_synchronize_cookie_domain(cookie, &async_running); } EXPORT_SYMBOL_GPL(async_synchronize_cookie); + +/** + * async_current_func - determine the async entry %current is executing + * @datap: optional out param for the data + * @cookiep: optional out param for the cookie + * + * Determine whether %current is an async worker executing an async_entry + * and if so return the async function and, if @cookiep is not %NULL, the + * cookie. If %current isn't executing an async_entry, %NULL is returned. + */ +async_func_ptr *async_current_func(void **datap, async_cookie_t *cookiep) +{ + struct work_struct *work; + struct async_entry *entry; + + if (work_current_func(&work) != async_run_entry_fn) + return NULL; + + entry = container_of(work, struct async_entry, work); + if (datap) + *datap = entry->data; + if (cookiep) + *cookiep = entry->cookie; + return entry->func; +} diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 6b99ac7..9fc1549 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -3492,6 +3492,28 @@ unsigned int work_busy(struct work_struct *work) } EXPORT_SYMBOL_GPL(work_busy); +/** + * work_current_func - determine the work fn and item %current is executing + * @workp: optional out param for the current work item + * + * Determine whether %current is a kworker executing a work item and if so + * return the work function and, if @workp is not %NULL, the work item. If + * %current isn't executing a work item, %NULL is returned. + */ +work_func_t work_current_func(struct work_struct **workp) +{ + struct worker *worker; + + /* am I a kworker? */ + if (!(current->flags & PF_WQ_WORKER)) + return NULL; + + worker = kthread_data(current); + if (workp) + *workp = worker->current_work; + return worker->current_func; +} + /* * CPU hotplug. * -- 1.8.0.2 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html