diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-07-30 01:27:13 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2015-07-31 01:06:29 -0400 |
commit | 9450918293b3c35f11883231a53da1aed2c78403 (patch) | |
tree | ed355995df7cb6b312afbad50afd570024e11529 | |
parent | c4cfdd81c8fde84e2c75bc90533c7e1276937d3a (diff) |
target: Perform RCU callback barrier before backend/fabric unload
This patch addresses a v4.2-rc1 regression where backend driver
module unload happening immediately after TBO->free_device()
does internal call_rcu(), will currently result in IRQ context
rcu_process_callbacks() use-after-free paging OOPsen.
It adds the missing rcu_barrier() in target_backend_unregister()
to perform an explicit RCU barrier waiting for all RCU callbacks
to complete before releasing target_backend_ops memory, and
allowing TBO->module exit to proceed.
Also, do the same for fabric drivers in target_unregister_template()
to ensure se_deve_entry->rcu_head -> kfree_rcu() callbacks have
completed, before allowing target_core_fabric_ops->owner module
exit to proceed.
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Hannes Reinecke <hare@suse.de>
Cc: Sagi Grimberg <sagig@mellanox.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
-rw-r--r-- | drivers/target/target_core_configfs.c | 9 | ||||
-rw-r--r-- | drivers/target/target_core_hba.c | 10 |
2 files changed, 17 insertions, 2 deletions
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index c2e9fea90b4a..860e84046177 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c | |||
@@ -457,8 +457,15 @@ void target_unregister_template(const struct target_core_fabric_ops *fo) | |||
457 | if (!strcmp(t->tf_ops->name, fo->name)) { | 457 | if (!strcmp(t->tf_ops->name, fo->name)) { |
458 | BUG_ON(atomic_read(&t->tf_access_cnt)); | 458 | BUG_ON(atomic_read(&t->tf_access_cnt)); |
459 | list_del(&t->tf_list); | 459 | list_del(&t->tf_list); |
460 | mutex_unlock(&g_tf_lock); | ||
461 | /* | ||
462 | * Wait for any outstanding fabric se_deve_entry->rcu_head | ||
463 | * callbacks to complete post kfree_rcu(), before allowing | ||
464 | * fabric driver unload of TFO->module to proceed. | ||
465 | */ | ||
466 | rcu_barrier(); | ||
460 | kfree(t); | 467 | kfree(t); |
461 | break; | 468 | return; |
462 | } | 469 | } |
463 | } | 470 | } |
464 | mutex_unlock(&g_tf_lock); | 471 | mutex_unlock(&g_tf_lock); |
diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index 62ea4e8e70a8..be9cefc07407 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c | |||
@@ -84,8 +84,16 @@ void target_backend_unregister(const struct target_backend_ops *ops) | |||
84 | list_for_each_entry(tb, &backend_list, list) { | 84 | list_for_each_entry(tb, &backend_list, list) { |
85 | if (tb->ops == ops) { | 85 | if (tb->ops == ops) { |
86 | list_del(&tb->list); | 86 | list_del(&tb->list); |
87 | mutex_unlock(&backend_mutex); | ||
88 | /* | ||
89 | * Wait for any outstanding backend driver ->rcu_head | ||
90 | * callbacks to complete post TBO->free_device() -> | ||
91 | * call_rcu(), before allowing backend driver module | ||
92 | * unload of target_backend_ops->owner to proceed. | ||
93 | */ | ||
94 | rcu_barrier(); | ||
87 | kfree(tb); | 95 | kfree(tb); |
88 | break; | 96 | return; |
89 | } | 97 | } |
90 | } | 98 | } |
91 | mutex_unlock(&backend_mutex); | 99 | mutex_unlock(&backend_mutex); |