aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2008-03-04 14:26:55 -0500
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-03-05 13:04:09 -0500
commit45ab33b6c190c4a8c58f1d13be2ff89ee62024ba (patch)
tree7bcec9986d93b55b0693829903a959e3c96f42b1 /drivers
parent024f801f528220edc89275a724ea00cd18c5ebb7 (diff)
[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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c76
1 files changed, 48 insertions, 28 deletions
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index dfb026b95a6a..ca7bb6f63bde 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -373,24 +373,25 @@ static void session_recovery_timedout(struct work_struct *work)
373 scsi_target_unblock(&session->dev); 373 scsi_target_unblock(&session->dev);
374} 374}
375 375
376static void __iscsi_unblock_session(struct iscsi_cls_session *session) 376static void __iscsi_unblock_session(struct work_struct *work)
377{
378 if (!cancel_delayed_work(&session->recovery_work))
379 flush_workqueue(iscsi_eh_timer_workq);
380 scsi_target_unblock(&session->dev);
381}
382
383void iscsi_unblock_session(struct iscsi_cls_session *session)
384{ 377{
378 struct iscsi_cls_session *session =
379 container_of(work, struct iscsi_cls_session,
380 unblock_work);
385 struct Scsi_Host *shost = iscsi_session_to_shost(session); 381 struct Scsi_Host *shost = iscsi_session_to_shost(session);
386 struct iscsi_host *ihost = shost->shost_data; 382 struct iscsi_host *ihost = shost->shost_data;
387 unsigned long flags; 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 spin_lock_irqsave(&session->lock, flags); 390 spin_lock_irqsave(&session->lock, flags);
390 session->state = ISCSI_SESSION_LOGGED_IN; 391 session->state = ISCSI_SESSION_LOGGED_IN;
391 spin_unlock_irqrestore(&session->lock, flags); 392 spin_unlock_irqrestore(&session->lock, flags);
392 393 /* start IO */
393 __iscsi_unblock_session(session); 394 scsi_target_unblock(&session->dev);
394 /* 395 /*
395 * Only do kernel scanning if the driver is properly hooked into 396 * Only do kernel scanning if the driver is properly hooked into
396 * the async scanning code (drivers like iscsi_tcp do login and 397 * the async scanning code (drivers like iscsi_tcp do login and
@@ -401,20 +402,43 @@ void iscsi_unblock_session(struct iscsi_cls_session *session)
401 atomic_inc(&ihost->nr_scans); 402 atomic_inc(&ihost->nr_scans);
402 } 403 }
403} 404}
405
406/**
407 * iscsi_unblock_session - set a session as logged in and start IO.
408 * @session: iscsi session
409 *
410 * Mark a session as ready to accept IO.
411 */
412void iscsi_unblock_session(struct iscsi_cls_session *session)
413{
414 queue_work(iscsi_eh_timer_workq, &session->unblock_work);
415 /*
416 * make sure all the events have completed before tell the driver
417 * it is safe
418 */
419 flush_workqueue(iscsi_eh_timer_workq);
420}
404EXPORT_SYMBOL_GPL(iscsi_unblock_session); 421EXPORT_SYMBOL_GPL(iscsi_unblock_session);
405 422
406void iscsi_block_session(struct iscsi_cls_session *session) 423static void __iscsi_block_session(struct work_struct *work)
407{ 424{
425 struct iscsi_cls_session *session =
426 container_of(work, struct iscsi_cls_session,
427 block_work);
408 unsigned long flags; 428 unsigned long flags;
409 429
410 spin_lock_irqsave(&session->lock, flags); 430 spin_lock_irqsave(&session->lock, flags);
411 session->state = ISCSI_SESSION_FAILED; 431 session->state = ISCSI_SESSION_FAILED;
412 spin_unlock_irqrestore(&session->lock, flags); 432 spin_unlock_irqrestore(&session->lock, flags);
413
414 scsi_target_block(&session->dev); 433 scsi_target_block(&session->dev);
415 queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, 434 queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
416 session->recovery_tmo * HZ); 435 session->recovery_tmo * HZ);
417} 436}
437
438void iscsi_block_session(struct iscsi_cls_session *session)
439{
440 queue_work(iscsi_eh_timer_workq, &session->block_work);
441}
418EXPORT_SYMBOL_GPL(iscsi_block_session); 442EXPORT_SYMBOL_GPL(iscsi_block_session);
419 443
420static void __iscsi_unbind_session(struct work_struct *work) 444static void __iscsi_unbind_session(struct work_struct *work)
@@ -463,6 +487,8 @@ iscsi_alloc_session(struct Scsi_Host *shost,
463 INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); 487 INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
464 INIT_LIST_HEAD(&session->host_list); 488 INIT_LIST_HEAD(&session->host_list);
465 INIT_LIST_HEAD(&session->sess_list); 489 INIT_LIST_HEAD(&session->sess_list);
490 INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
491 INIT_WORK(&session->block_work, __iscsi_block_session);
466 INIT_WORK(&session->unbind_work, __iscsi_unbind_session); 492 INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
467 INIT_WORK(&session->scan_work, iscsi_scan_session); 493 INIT_WORK(&session->scan_work, iscsi_scan_session);
468 spin_lock_init(&session->lock); 494 spin_lock_init(&session->lock);
@@ -575,24 +601,25 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
575 list_del(&session->sess_list); 601 list_del(&session->sess_list);
576 spin_unlock_irqrestore(&sesslock, flags); 602 spin_unlock_irqrestore(&sesslock, flags);
577 603
604 /* make sure there are no blocks/unblocks queued */
605 flush_workqueue(iscsi_eh_timer_workq);
606 /* make sure the timedout callout is not running */
607 if (!cancel_delayed_work(&session->recovery_work))
608 flush_workqueue(iscsi_eh_timer_workq);
578 /* 609 /*
579 * If we are blocked let commands flow again. The lld or iscsi 610 * If we are blocked let commands flow again. The lld or iscsi
580 * layer should set up the queuecommand to fail commands. 611 * layer should set up the queuecommand to fail commands.
612 * We assume that LLD will not be calling block/unblock while
613 * removing the session.
581 */ 614 */
582 spin_lock_irqsave(&session->lock, flags); 615 spin_lock_irqsave(&session->lock, flags);
583 session->state = ISCSI_SESSION_FREE; 616 session->state = ISCSI_SESSION_FREE;
584 spin_unlock_irqrestore(&session->lock, flags); 617 spin_unlock_irqrestore(&session->lock, flags);
585 __iscsi_unblock_session(session);
586 __iscsi_unbind_session(&session->unbind_work);
587 618
588 /* flush running scans */ 619 scsi_target_unblock(&session->dev);
620 /* flush running scans then delete devices */
589 flush_workqueue(ihost->scan_workq); 621 flush_workqueue(ihost->scan_workq);
590 /* 622 __iscsi_unbind_session(&session->unbind_work);
591 * If the session dropped while removing devices then we need to make
592 * sure it is not blocked
593 */
594 if (!cancel_delayed_work(&session->recovery_work))
595 flush_workqueue(iscsi_eh_timer_workq);
596 623
597 /* hw iscsi may not have removed all connections from session */ 624 /* hw iscsi may not have removed all connections from session */
598 err = device_for_each_child(&session->dev, NULL, 625 err = device_for_each_child(&session->dev, NULL,
@@ -802,23 +829,16 @@ EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
802 829
803void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) 830void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
804{ 831{
805 struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
806 struct nlmsghdr *nlh; 832 struct nlmsghdr *nlh;
807 struct sk_buff *skb; 833 struct sk_buff *skb;
808 struct iscsi_uevent *ev; 834 struct iscsi_uevent *ev;
809 struct iscsi_internal *priv; 835 struct iscsi_internal *priv;
810 int len = NLMSG_SPACE(sizeof(*ev)); 836 int len = NLMSG_SPACE(sizeof(*ev));
811 unsigned long flags;
812 837
813 priv = iscsi_if_transport_lookup(conn->transport); 838 priv = iscsi_if_transport_lookup(conn->transport);
814 if (!priv) 839 if (!priv)
815 return; 840 return;
816 841
817 spin_lock_irqsave(&session->lock, flags);
818 if (session->state == ISCSI_SESSION_LOGGED_IN)
819 session->state = ISCSI_SESSION_FAILED;
820 spin_unlock_irqrestore(&session->lock, flags);
821
822 skb = alloc_skb(len, GFP_ATOMIC); 842 skb = alloc_skb(len, GFP_ATOMIC);
823 if (!skb) { 843 if (!skb) {
824 iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored " 844 iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "