diff options
author | Andy Grover <agrover@redhat.com> | 2014-01-24 19:18:54 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-06 14:08:17 -0500 |
commit | 85c3c54bf969f0fd9ba59ee3f6e9849b53428412 (patch) | |
tree | aebb442573f42166a6948200d417e82d05182518 | |
parent | 26996fcd25dda2f6d34d1888d26585cd519f6af5 (diff) |
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>
-rw-r--r-- | drivers/target/iscsi/iscsi_target.c | 34 |
1 files changed, 21 insertions, 13 deletions
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index b71a69750607..5b07fd156bd7 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c | |||
@@ -54,7 +54,7 @@ | |||
54 | static LIST_HEAD(g_tiqn_list); | 54 | static LIST_HEAD(g_tiqn_list); |
55 | static LIST_HEAD(g_np_list); | 55 | static LIST_HEAD(g_np_list); |
56 | static DEFINE_SPINLOCK(tiqn_lock); | 56 | static DEFINE_SPINLOCK(tiqn_lock); |
57 | static DEFINE_SPINLOCK(np_lock); | 57 | static DEFINE_MUTEX(np_lock); |
58 | 58 | ||
59 | static struct idr tiqn_idr; | 59 | static struct idr tiqn_idr; |
60 | struct idr sess_idr; | 60 | struct idr sess_idr; |
@@ -303,6 +303,9 @@ bool iscsit_check_np_match( | |||
303 | return false; | 303 | return false; |
304 | } | 304 | } |
305 | 305 | ||
306 | /* | ||
307 | * Called with mutex np_lock held | ||
308 | */ | ||
306 | static struct iscsi_np *iscsit_get_np( | 309 | static struct iscsi_np *iscsit_get_np( |
307 | struct __kernel_sockaddr_storage *sockaddr, | 310 | struct __kernel_sockaddr_storage *sockaddr, |
308 | int network_transport) | 311 | int network_transport) |
@@ -310,11 +313,10 @@ static struct iscsi_np *iscsit_get_np( | |||
310 | struct iscsi_np *np; | 313 | struct iscsi_np *np; |
311 | bool match; | 314 | bool match; |
312 | 315 | ||
313 | spin_lock_bh(&np_lock); | ||
314 | list_for_each_entry(np, &g_np_list, np_list) { | 316 | list_for_each_entry(np, &g_np_list, np_list) { |
315 | spin_lock(&np->np_thread_lock); | 317 | spin_lock_bh(&np->np_thread_lock); |
316 | if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { | 318 | if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { |
317 | spin_unlock(&np->np_thread_lock); | 319 | spin_unlock_bh(&np->np_thread_lock); |
318 | continue; | 320 | continue; |
319 | } | 321 | } |
320 | 322 | ||
@@ -326,13 +328,11 @@ static struct iscsi_np *iscsit_get_np( | |||
326 | * while iscsi_tpg_add_network_portal() is called. | 328 | * while iscsi_tpg_add_network_portal() is called. |
327 | */ | 329 | */ |
328 | np->np_exports++; | 330 | np->np_exports++; |
329 | spin_unlock(&np->np_thread_lock); | 331 | spin_unlock_bh(&np->np_thread_lock); |
330 | spin_unlock_bh(&np_lock); | ||
331 | return np; | 332 | return np; |
332 | } | 333 | } |
333 | spin_unlock(&np->np_thread_lock); | 334 | spin_unlock_bh(&np->np_thread_lock); |
334 | } | 335 | } |
335 | spin_unlock_bh(&np_lock); | ||
336 | 336 | ||
337 | return NULL; | 337 | return NULL; |
338 | } | 338 | } |
@@ -346,16 +346,22 @@ struct iscsi_np *iscsit_add_np( | |||
346 | struct sockaddr_in6 *sock_in6; | 346 | struct sockaddr_in6 *sock_in6; |
347 | struct iscsi_np *np; | 347 | struct iscsi_np *np; |
348 | int ret; | 348 | int ret; |
349 | |||
350 | mutex_lock(&np_lock); | ||
351 | |||
349 | /* | 352 | /* |
350 | * Locate the existing struct iscsi_np if already active.. | 353 | * Locate the existing struct iscsi_np if already active.. |
351 | */ | 354 | */ |
352 | np = iscsit_get_np(sockaddr, network_transport); | 355 | np = iscsit_get_np(sockaddr, network_transport); |
353 | if (np) | 356 | if (np) { |
357 | mutex_unlock(&np_lock); | ||
354 | return np; | 358 | return np; |
359 | } | ||
355 | 360 | ||
356 | np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL); | 361 | np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL); |
357 | if (!np) { | 362 | if (!np) { |
358 | pr_err("Unable to allocate memory for struct iscsi_np\n"); | 363 | pr_err("Unable to allocate memory for struct iscsi_np\n"); |
364 | mutex_unlock(&np_lock); | ||
359 | return ERR_PTR(-ENOMEM); | 365 | return ERR_PTR(-ENOMEM); |
360 | } | 366 | } |
361 | 367 | ||
@@ -378,6 +384,7 @@ struct iscsi_np *iscsit_add_np( | |||
378 | ret = iscsi_target_setup_login_socket(np, sockaddr); | 384 | ret = iscsi_target_setup_login_socket(np, sockaddr); |
379 | if (ret != 0) { | 385 | if (ret != 0) { |
380 | kfree(np); | 386 | kfree(np); |
387 | mutex_unlock(&np_lock); | ||
381 | return ERR_PTR(ret); | 388 | return ERR_PTR(ret); |
382 | } | 389 | } |
383 | 390 | ||
@@ -386,6 +393,7 @@ struct iscsi_np *iscsit_add_np( | |||
386 | pr_err("Unable to create kthread: iscsi_np\n"); | 393 | pr_err("Unable to create kthread: iscsi_np\n"); |
387 | ret = PTR_ERR(np->np_thread); | 394 | ret = PTR_ERR(np->np_thread); |
388 | kfree(np); | 395 | kfree(np); |
396 | mutex_unlock(&np_lock); | ||
389 | return ERR_PTR(ret); | 397 | return ERR_PTR(ret); |
390 | } | 398 | } |
391 | /* | 399 | /* |
@@ -396,10 +404,10 @@ struct iscsi_np *iscsit_add_np( | |||
396 | * point because iscsi_np has not been added to g_np_list yet. | 404 | * point because iscsi_np has not been added to g_np_list yet. |
397 | */ | 405 | */ |
398 | np->np_exports = 1; | 406 | np->np_exports = 1; |
407 | np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; | ||
399 | 408 | ||
400 | spin_lock_bh(&np_lock); | ||
401 | list_add_tail(&np->np_list, &g_np_list); | 409 | list_add_tail(&np->np_list, &g_np_list); |
402 | spin_unlock_bh(&np_lock); | 410 | mutex_unlock(&np_lock); |
403 | 411 | ||
404 | pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", | 412 | pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", |
405 | np->np_ip, np->np_port, np->np_transport->name); | 413 | np->np_ip, np->np_port, np->np_transport->name); |
@@ -469,9 +477,9 @@ int iscsit_del_np(struct iscsi_np *np) | |||
469 | 477 | ||
470 | np->np_transport->iscsit_free_np(np); | 478 | np->np_transport->iscsit_free_np(np); |
471 | 479 | ||
472 | spin_lock_bh(&np_lock); | 480 | mutex_lock(&np_lock); |
473 | list_del(&np->np_list); | 481 | list_del(&np->np_list); |
474 | spin_unlock_bh(&np_lock); | 482 | mutex_unlock(&np_lock); |
475 | 483 | ||
476 | pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", | 484 | pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", |
477 | np->np_ip, np->np_port, np->np_transport->name); | 485 | np->np_ip, np->np_port, np->np_transport->name); |