target/iscsi: Fix network portal creation race

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>
Cc: <stable@vger.kernel.org> #3.1+
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>

authored by Andy Grover and committed by Nicholas Bellinger ee291e63 76736db3

+21 -13
+21 -13
drivers/target/iscsi/iscsi_target.c
··· 52 52 static LIST_HEAD(g_tiqn_list); 53 53 static LIST_HEAD(g_np_list); 54 54 static DEFINE_SPINLOCK(tiqn_lock); 55 - static DEFINE_SPINLOCK(np_lock); 55 + static DEFINE_MUTEX(np_lock); 56 56 57 57 static struct idr tiqn_idr; 58 58 struct idr sess_idr; ··· 307 307 return false; 308 308 } 309 309 310 + /* 311 + * Called with mutex np_lock held 312 + */ 310 313 static struct iscsi_np *iscsit_get_np( 311 314 struct __kernel_sockaddr_storage *sockaddr, 312 315 int network_transport) ··· 317 314 struct iscsi_np *np; 318 315 bool match; 319 316 320 - spin_lock_bh(&np_lock); 321 317 list_for_each_entry(np, &g_np_list, np_list) { 322 - spin_lock(&np->np_thread_lock); 318 + spin_lock_bh(&np->np_thread_lock); 323 319 if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { 324 - spin_unlock(&np->np_thread_lock); 320 + spin_unlock_bh(&np->np_thread_lock); 325 321 continue; 326 322 } 327 323 ··· 332 330 * while iscsi_tpg_add_network_portal() is called. 333 331 */ 334 332 np->np_exports++; 335 - spin_unlock(&np->np_thread_lock); 336 - spin_unlock_bh(&np_lock); 333 + spin_unlock_bh(&np->np_thread_lock); 337 334 return np; 338 335 } 339 - spin_unlock(&np->np_thread_lock); 336 + spin_unlock_bh(&np->np_thread_lock); 340 337 } 341 - spin_unlock_bh(&np_lock); 342 338 343 339 return NULL; 344 340 } ··· 350 350 struct sockaddr_in6 *sock_in6; 351 351 struct iscsi_np *np; 352 352 int ret; 353 + 354 + mutex_lock(&np_lock); 355 + 353 356 /* 354 357 * Locate the existing struct iscsi_np if already active.. 355 358 */ 356 359 np = iscsit_get_np(sockaddr, network_transport); 357 - if (np) 360 + if (np) { 361 + mutex_unlock(&np_lock); 358 362 return np; 363 + } 359 364 360 365 np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL); 361 366 if (!np) { 362 367 pr_err("Unable to allocate memory for struct iscsi_np\n"); 368 + mutex_unlock(&np_lock); 363 369 return ERR_PTR(-ENOMEM); 364 370 } 365 371 ··· 388 382 ret = iscsi_target_setup_login_socket(np, sockaddr); 389 383 if (ret != 0) { 390 384 kfree(np); 385 + mutex_unlock(&np_lock); 391 386 return ERR_PTR(ret); 392 387 } 393 388 ··· 397 390 pr_err("Unable to create kthread: iscsi_np\n"); 398 391 ret = PTR_ERR(np->np_thread); 399 392 kfree(np); 393 + mutex_unlock(&np_lock); 400 394 return ERR_PTR(ret); 401 395 } 402 396 /* ··· 408 400 * point because iscsi_np has not been added to g_np_list yet. 409 401 */ 410 402 np->np_exports = 1; 403 + np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; 411 404 412 - spin_lock_bh(&np_lock); 413 405 list_add_tail(&np->np_list, &g_np_list); 414 - spin_unlock_bh(&np_lock); 406 + mutex_unlock(&np_lock); 415 407 416 408 pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", 417 409 np->np_ip, np->np_port, np->np_transport->name); ··· 477 469 478 470 np->np_transport->iscsit_free_np(np); 479 471 480 - spin_lock_bh(&np_lock); 472 + mutex_lock(&np_lock); 481 473 list_del(&np->np_list); 482 - spin_unlock_bh(&np_lock); 474 + mutex_unlock(&np_lock); 483 475 484 476 pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", 485 477 np->np_ip, np->np_port, np->np_transport->name);