[SCSI] iscsi class: regression - fix races with state manipulation and blocking/unblocking

For qla4xxx, we could be starting a session, but some error (network,
target, IO from a device that got started, etc) could cause the session
to fail and curring the block/unblock and state manipulation could race
with each other. This patch just has those operations done in the
single threaded iscsi eh work queue, so that way they are serialized.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

authored by Mike Christie and committed by James Bottomley 45ab33b6 024f801f

+50 -28
+48 -28
drivers/scsi/scsi_transport_iscsi.c
··· 373 373 scsi_target_unblock(&session->dev); 374 374 } 375 375 376 - static void __iscsi_unblock_session(struct iscsi_cls_session *session) 376 + static void __iscsi_unblock_session(struct work_struct *work) 377 377 { 378 - if (!cancel_delayed_work(&session->recovery_work)) 379 - flush_workqueue(iscsi_eh_timer_workq); 380 - scsi_target_unblock(&session->dev); 381 - } 382 - 383 - void iscsi_unblock_session(struct iscsi_cls_session *session) 384 - { 378 + struct iscsi_cls_session *session = 379 + container_of(work, struct iscsi_cls_session, 380 + unblock_work); 385 381 struct Scsi_Host *shost = iscsi_session_to_shost(session); 386 382 struct iscsi_host *ihost = shost->shost_data; 387 383 unsigned long flags; 388 384 385 + /* 386 + * The recovery and unblock work get run from the same workqueue, 387 + * so try to cancel it if it was going to run after this unblock. 388 + */ 389 + cancel_delayed_work(&session->recovery_work); 389 390 spin_lock_irqsave(&session->lock, flags); 390 391 session->state = ISCSI_SESSION_LOGGED_IN; 391 392 spin_unlock_irqrestore(&session->lock, flags); 392 - 393 - __iscsi_unblock_session(session); 393 + /* start IO */ 394 + scsi_target_unblock(&session->dev); 394 395 /* 395 396 * Only do kernel scanning if the driver is properly hooked into 396 397 * the async scanning code (drivers like iscsi_tcp do login and ··· 402 401 atomic_inc(&ihost->nr_scans); 403 402 } 404 403 } 404 + 405 + /** 406 + * iscsi_unblock_session - set a session as logged in and start IO. 407 + * @session: iscsi session 408 + * 409 + * Mark a session as ready to accept IO. 410 + */ 411 + void iscsi_unblock_session(struct iscsi_cls_session *session) 412 + { 413 + queue_work(iscsi_eh_timer_workq, &session->unblock_work); 414 + /* 415 + * make sure all the events have completed before tell the driver 416 + * it is safe 417 + */ 418 + flush_workqueue(iscsi_eh_timer_workq); 419 + } 405 420 EXPORT_SYMBOL_GPL(iscsi_unblock_session); 406 421 407 - void iscsi_block_session(struct iscsi_cls_session *session) 422 + static void __iscsi_block_session(struct work_struct *work) 408 423 { 424 + struct iscsi_cls_session *session = 425 + container_of(work, struct iscsi_cls_session, 426 + block_work); 409 427 unsigned long flags; 410 428 411 429 spin_lock_irqsave(&session->lock, flags); 412 430 session->state = ISCSI_SESSION_FAILED; 413 431 spin_unlock_irqrestore(&session->lock, flags); 414 - 415 432 scsi_target_block(&session->dev); 416 433 queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, 417 434 session->recovery_tmo * HZ); 435 + } 436 + 437 + void iscsi_block_session(struct iscsi_cls_session *session) 438 + { 439 + queue_work(iscsi_eh_timer_workq, &session->block_work); 418 440 } 419 441 EXPORT_SYMBOL_GPL(iscsi_block_session); 420 442 ··· 487 463 INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); 488 464 INIT_LIST_HEAD(&session->host_list); 489 465 INIT_LIST_HEAD(&session->sess_list); 466 + INIT_WORK(&session->unblock_work, __iscsi_unblock_session); 467 + INIT_WORK(&session->block_work, __iscsi_block_session); 490 468 INIT_WORK(&session->unbind_work, __iscsi_unbind_session); 491 469 INIT_WORK(&session->scan_work, iscsi_scan_session); 492 470 spin_lock_init(&session->lock); ··· 601 575 list_del(&session->sess_list); 602 576 spin_unlock_irqrestore(&sesslock, flags); 603 577 578 + /* make sure there are no blocks/unblocks queued */ 579 + flush_workqueue(iscsi_eh_timer_workq); 580 + /* make sure the timedout callout is not running */ 581 + if (!cancel_delayed_work(&session->recovery_work)) 582 + flush_workqueue(iscsi_eh_timer_workq); 604 583 /* 605 584 * If we are blocked let commands flow again. The lld or iscsi 606 585 * layer should set up the queuecommand to fail commands. 586 + * We assume that LLD will not be calling block/unblock while 587 + * removing the session. 607 588 */ 608 589 spin_lock_irqsave(&session->lock, flags); 609 590 session->state = ISCSI_SESSION_FREE; 610 591 spin_unlock_irqrestore(&session->lock, flags); 611 - __iscsi_unblock_session(session); 612 - __iscsi_unbind_session(&session->unbind_work); 613 592 614 - /* flush running scans */ 593 + scsi_target_unblock(&session->dev); 594 + /* flush running scans then delete devices */ 615 595 flush_workqueue(ihost->scan_workq); 616 - /* 617 - * If the session dropped while removing devices then we need to make 618 - * sure it is not blocked 619 - */ 620 - if (!cancel_delayed_work(&session->recovery_work)) 621 - flush_workqueue(iscsi_eh_timer_workq); 596 + __iscsi_unbind_session(&session->unbind_work); 622 597 623 598 /* hw iscsi may not have removed all connections from session */ 624 599 err = device_for_each_child(&session->dev, NULL, ··· 829 802 830 803 void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) 831 804 { 832 - struct iscsi_cls_session *session = iscsi_conn_to_session(conn); 833 805 struct nlmsghdr *nlh; 834 806 struct sk_buff *skb; 835 807 struct iscsi_uevent *ev; 836 808 struct iscsi_internal *priv; 837 809 int len = NLMSG_SPACE(sizeof(*ev)); 838 - unsigned long flags; 839 810 840 811 priv = iscsi_if_transport_lookup(conn->transport); 841 812 if (!priv) 842 813 return; 843 - 844 - spin_lock_irqsave(&session->lock, flags); 845 - if (session->state == ISCSI_SESSION_LOGGED_IN) 846 - session->state = ISCSI_SESSION_FAILED; 847 - spin_unlock_irqrestore(&session->lock, flags); 848 814 849 815 skb = alloc_skb(len, GFP_ATOMIC); 850 816 if (!skb) {
+2
include/scsi/scsi_transport_iscsi.h
··· 177 177 struct list_head host_list; 178 178 struct iscsi_transport *transport; 179 179 spinlock_t lock; 180 + struct work_struct block_work; 181 + struct work_struct unblock_work; 180 182 struct work_struct scan_work; 181 183 struct work_struct unbind_work; 182 184