target: Perform RCU callback barrier before backend/fabric unload
authorNicholas Bellinger <nab@linux-iscsi.org>
Thu, 30 Jul 2015 05:27:13 +0000 (22:27 -0700)
committerNicholas Bellinger <nab@linux-iscsi.org>
Fri, 31 Jul 2015 05:06:29 +0000 (22:06 -0700)
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>
drivers/target/target_core_configfs.c
drivers/target/target_core_hba.c

index c2e9fea..860e840 100644 (file)
@@ -457,8 +457,15 @@ void target_unregister_template(const struct target_core_fabric_ops *fo)
                if (!strcmp(t->tf_ops->name, fo->name)) {
                        BUG_ON(atomic_read(&t->tf_access_cnt));
                        list_del(&t->tf_list);
+                       mutex_unlock(&g_tf_lock);
+                       /*
+                        * Wait for any outstanding fabric se_deve_entry->rcu_head
+                        * callbacks to complete post kfree_rcu(), before allowing
+                        * fabric driver unload of TFO->module to proceed.
+                        */
+                       rcu_barrier();
                        kfree(t);
-                       break;
+                       return;
                }
        }
        mutex_unlock(&g_tf_lock);
index 62ea4e8..be9cefc 100644 (file)
@@ -84,8 +84,16 @@ void target_backend_unregister(const struct target_backend_ops *ops)
        list_for_each_entry(tb, &backend_list, list) {
                if (tb->ops == ops) {
                        list_del(&tb->list);
+                       mutex_unlock(&backend_mutex);
+                       /*
+                        * Wait for any outstanding backend driver ->rcu_head
+                        * callbacks to complete post TBO->free_device() ->
+                        * call_rcu(), before allowing backend driver module
+                        * unload of target_backend_ops->owner to proceed.
+                        */
+                       rcu_barrier();
                        kfree(tb);
-                       break;
+                       return;
                }
        }
        mutex_unlock(&backend_mutex);