summaryrefslogtreecommitdiff
path: root/drivers/target
diff options
context:
space:
mode:
authorAndy Grover <agrover@redhat.com>2014-01-24 16:18:54 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-02-06 11:08:17 -0800
commit85c3c54bf969f0fd9ba59ee3f6e9849b53428412 (patch)
treeaebb442573f42166a6948200d417e82d05182518 /drivers/target
parent26996fcd25dda2f6d34d1888d26585cd519f6af5 (diff)
downloadlinux-3.10-85c3c54bf969f0fd9ba59ee3f6e9849b53428412.tar.gz
linux-3.10-85c3c54bf969f0fd9ba59ee3f6e9849b53428412.tar.bz2
linux-3.10-85c3c54bf969f0fd9ba59ee3f6e9849b53428412.zip
target/iscsi: Fix network portal creation race
commit ee291e63293146db64668e8d65eb35c97e8324f4 upstream. When creating network portals rapidly, such as when restoring a configuration, LIO's code to reuse existing portals can return a false negative if the thread hasn't run yet and set np_thread_state to ISCSI_NP_THREAD_ACTIVE. This causes an error in the network stack when attempting to bind to the same address/port. This patch sets NP_THREAD_ACTIVE before the np is placed on g_np_list, so even if the thread hasn't run yet, iscsit_get_np will return the existing np. Also, convert np_lock -> np_mutex + hold across adding new net portal to g_np_list to prevent a race where two threads may attempt to create the same network portal, resulting in one of them failing. (nab: Add missing mutex_unlocks in iscsit_add_np failure paths) (DanC: Fix incorrect spin_unlock -> spin_unlock_bh) Signed-off-by: Andy Grover <agrover@redhat.com> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/target')
-rw-r--r--drivers/target/iscsi/iscsi_target.c34
1 files changed, 21 insertions, 13 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index b71a6975060..5b07fd156bd 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -54,7 +54,7 @@
static LIST_HEAD(g_tiqn_list);
static LIST_HEAD(g_np_list);
static DEFINE_SPINLOCK(tiqn_lock);
-static DEFINE_SPINLOCK(np_lock);
+static DEFINE_MUTEX(np_lock);
static struct idr tiqn_idr;
struct idr sess_idr;
@@ -303,6 +303,9 @@ bool iscsit_check_np_match(
return false;
}
+/*
+ * Called with mutex np_lock held
+ */
static struct iscsi_np *iscsit_get_np(
struct __kernel_sockaddr_storage *sockaddr,
int network_transport)
@@ -310,11 +313,10 @@ static struct iscsi_np *iscsit_get_np(
struct iscsi_np *np;
bool match;
- spin_lock_bh(&np_lock);
list_for_each_entry(np, &g_np_list, np_list) {
- spin_lock(&np->np_thread_lock);
+ spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
- spin_unlock(&np->np_thread_lock);
+ spin_unlock_bh(&np->np_thread_lock);
continue;
}
@@ -326,13 +328,11 @@ static struct iscsi_np *iscsit_get_np(
* while iscsi_tpg_add_network_portal() is called.
*/
np->np_exports++;
- spin_unlock(&np->np_thread_lock);
- spin_unlock_bh(&np_lock);
+ spin_unlock_bh(&np->np_thread_lock);
return np;
}
- spin_unlock(&np->np_thread_lock);
+ spin_unlock_bh(&np->np_thread_lock);
}
- spin_unlock_bh(&np_lock);
return NULL;
}
@@ -346,16 +346,22 @@ struct iscsi_np *iscsit_add_np(
struct sockaddr_in6 *sock_in6;
struct iscsi_np *np;
int ret;
+
+ mutex_lock(&np_lock);
+
/*
* Locate the existing struct iscsi_np if already active..
*/
np = iscsit_get_np(sockaddr, network_transport);
- if (np)
+ if (np) {
+ mutex_unlock(&np_lock);
return np;
+ }
np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL);
if (!np) {
pr_err("Unable to allocate memory for struct iscsi_np\n");
+ mutex_unlock(&np_lock);
return ERR_PTR(-ENOMEM);
}
@@ -378,6 +384,7 @@ struct iscsi_np *iscsit_add_np(
ret = iscsi_target_setup_login_socket(np, sockaddr);
if (ret != 0) {
kfree(np);
+ mutex_unlock(&np_lock);
return ERR_PTR(ret);
}
@@ -386,6 +393,7 @@ struct iscsi_np *iscsit_add_np(
pr_err("Unable to create kthread: iscsi_np\n");
ret = PTR_ERR(np->np_thread);
kfree(np);
+ mutex_unlock(&np_lock);
return ERR_PTR(ret);
}
/*
@@ -396,10 +404,10 @@ struct iscsi_np *iscsit_add_np(
* point because iscsi_np has not been added to g_np_list yet.
*/
np->np_exports = 1;
+ np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
- spin_lock_bh(&np_lock);
list_add_tail(&np->np_list, &g_np_list);
- spin_unlock_bh(&np_lock);
+ mutex_unlock(&np_lock);
pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n",
np->np_ip, np->np_port, np->np_transport->name);
@@ -469,9 +477,9 @@ int iscsit_del_np(struct iscsi_np *np)
np->np_transport->iscsit_free_np(np);
- spin_lock_bh(&np_lock);
+ mutex_lock(&np_lock);
list_del(&np->np_list);
- spin_unlock_bh(&np_lock);
+ mutex_unlock(&np_lock);
pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n",
np->np_ip, np->np_port, np->np_transport->name);