diff options
author | Chris Leech <christopher.leech@intel.com> | 2009-02-27 13:56:32 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-10 10:09:40 -0400 |
commit | 55c8bafba549e3e82f6999db8c5a62fc3c255c14 (patch) | |
tree | 3ae4d7375fc1c5faaa16cc70277ca24e33a6f9b0 /drivers/scsi | |
parent | c826a3145736e3baabebccfd0aecfbb6dae059f2 (diff) |
[SCSI] fcoe: fix handling of pending queue, prevent out of order frames (v3)
In fcoe_check_wait_queue() the queue length could temporarily drop to 0,
before the last frame was successfully sent. This resulted in out of order
data frames within a single sequence, leading to IO timeout errors.
This builds on the approach from Vasu Dev to only fix the queue management in
fcoe_check_wait_queue, where my first patch added locking to the transmit
path even when the pending queue was not in use.
This patch continues to use fcoe_pending_queue.qlen instead of introducing a
new length counter, but takes precautions to ensure it never drops to 0 before
the final frame in the queue has successfully been passed to the netdev qdisc
layer. It also includes some cleanup of fcoe_check_wait_queue and removes the
fcoe_insert_wait_queue(_head) wrapper functions.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/fcoe/libfcoe.c | 81 |
1 files changed, 24 insertions, 57 deletions
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 14dd8a0402b6..0e0b494fc17c 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c | |||
@@ -70,8 +70,6 @@ struct fcoe_percpu_s *fcoe_percpu[NR_CPUS]; | |||
70 | 70 | ||
71 | /* Function Prototyes */ | 71 | /* Function Prototyes */ |
72 | static int fcoe_check_wait_queue(struct fc_lport *); | 72 | static int fcoe_check_wait_queue(struct fc_lport *); |
73 | static void fcoe_insert_wait_queue_head(struct fc_lport *, struct sk_buff *); | ||
74 | static void fcoe_insert_wait_queue(struct fc_lport *, struct sk_buff *); | ||
75 | static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *); | 73 | static void fcoe_recv_flogi(struct fcoe_softc *, struct fc_frame *, u8 *); |
76 | #ifdef CONFIG_HOTPLUG_CPU | 74 | #ifdef CONFIG_HOTPLUG_CPU |
77 | static int fcoe_cpu_callback(struct notifier_block *, ulong, void *); | 75 | static int fcoe_cpu_callback(struct notifier_block *, ulong, void *); |
@@ -501,7 +499,9 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) | |||
501 | rc = fcoe_start_io(skb); | 499 | rc = fcoe_start_io(skb); |
502 | 500 | ||
503 | if (rc) { | 501 | if (rc) { |
504 | fcoe_insert_wait_queue(lp, skb); | 502 | spin_lock_bh(&fc->fcoe_pending_queue.lock); |
503 | __skb_queue_tail(&fc->fcoe_pending_queue, skb); | ||
504 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
505 | if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) | 505 | if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) |
506 | lp->qfull = 1; | 506 | lp->qfull = 1; |
507 | } | 507 | } |
@@ -756,33 +756,36 @@ void fcoe_watchdog(ulong vp) | |||
756 | */ | 756 | */ |
757 | static int fcoe_check_wait_queue(struct fc_lport *lp) | 757 | static int fcoe_check_wait_queue(struct fc_lport *lp) |
758 | { | 758 | { |
759 | struct fcoe_softc *fc = lport_priv(lp); | ||
759 | struct sk_buff *skb; | 760 | struct sk_buff *skb; |
760 | struct fcoe_softc *fc; | ||
761 | int rc = -1; | 761 | int rc = -1; |
762 | 762 | ||
763 | fc = lport_priv(lp); | ||
764 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | 763 | spin_lock_bh(&fc->fcoe_pending_queue.lock); |
765 | |||
766 | if (fc->fcoe_pending_queue_active) | 764 | if (fc->fcoe_pending_queue_active) |
767 | goto out; | 765 | goto out; |
768 | fc->fcoe_pending_queue_active = 1; | 766 | fc->fcoe_pending_queue_active = 1; |
769 | if (fc->fcoe_pending_queue.qlen) { | 767 | |
770 | while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { | 768 | while (fc->fcoe_pending_queue.qlen) { |
771 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | 769 | /* keep qlen > 0 until fcoe_start_io succeeds */ |
772 | rc = fcoe_start_io(skb); | 770 | fc->fcoe_pending_queue.qlen++; |
773 | if (rc) | 771 | skb = __skb_dequeue(&fc->fcoe_pending_queue); |
774 | fcoe_insert_wait_queue_head(lp, skb); | 772 | |
775 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | 773 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); |
776 | if (rc) | 774 | rc = fcoe_start_io(skb); |
777 | break; | 775 | spin_lock_bh(&fc->fcoe_pending_queue.lock); |
776 | |||
777 | if (rc) { | ||
778 | __skb_queue_head(&fc->fcoe_pending_queue, skb); | ||
779 | /* undo temporary increment above */ | ||
780 | fc->fcoe_pending_queue.qlen--; | ||
781 | break; | ||
778 | } | 782 | } |
779 | /* | 783 | /* undo temporary increment above */ |
780 | * if interface pending queue is below FCOE_LOW_QUEUE_DEPTH | 784 | fc->fcoe_pending_queue.qlen--; |
781 | * then clear qfull flag. | ||
782 | */ | ||
783 | if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) | ||
784 | lp->qfull = 0; | ||
785 | } | 785 | } |
786 | |||
787 | if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) | ||
788 | lp->qfull = 0; | ||
786 | fc->fcoe_pending_queue_active = 0; | 789 | fc->fcoe_pending_queue_active = 0; |
787 | rc = fc->fcoe_pending_queue.qlen; | 790 | rc = fc->fcoe_pending_queue.qlen; |
788 | out: | 791 | out: |
@@ -791,42 +794,6 @@ out: | |||
791 | } | 794 | } |
792 | 795 | ||
793 | /** | 796 | /** |
794 | * fcoe_insert_wait_queue_head() - puts skb to fcoe pending queue head | ||
795 | * @lp: the fc_port for this skb | ||
796 | * @skb: the associated skb to be xmitted | ||
797 | * | ||
798 | * Returns: none | ||
799 | */ | ||
800 | static void fcoe_insert_wait_queue_head(struct fc_lport *lp, | ||
801 | struct sk_buff *skb) | ||
802 | { | ||
803 | struct fcoe_softc *fc; | ||
804 | |||
805 | fc = lport_priv(lp); | ||
806 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
807 | __skb_queue_head(&fc->fcoe_pending_queue, skb); | ||
808 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
809 | } | ||
810 | |||
811 | /** | ||
812 | * fcoe_insert_wait_queue() - put the skb into fcoe pending queue tail | ||
813 | * @lp: the fc_port for this skb | ||
814 | * @skb: the associated skb to be xmitted | ||
815 | * | ||
816 | * Returns: none | ||
817 | */ | ||
818 | static void fcoe_insert_wait_queue(struct fc_lport *lp, | ||
819 | struct sk_buff *skb) | ||
820 | { | ||
821 | struct fcoe_softc *fc; | ||
822 | |||
823 | fc = lport_priv(lp); | ||
824 | spin_lock_bh(&fc->fcoe_pending_queue.lock); | ||
825 | __skb_queue_tail(&fc->fcoe_pending_queue, skb); | ||
826 | spin_unlock_bh(&fc->fcoe_pending_queue.lock); | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * fcoe_dev_setup() - setup link change notification interface | 797 | * fcoe_dev_setup() - setup link change notification interface |
831 | */ | 798 | */ |
832 | static void fcoe_dev_setup() | 799 | static void fcoe_dev_setup() |