diff options
author | Bryan O'Sullivan <bos@pathscale.com> | 2006-07-01 07:36:05 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-07-01 12:56:00 -0400 |
commit | 57abad25f844e760082c0b1ab2b176dad682ea16 (patch) | |
tree | 5a61c70ed4ae1ad7ccb8c246c22480d8bb045f46 | |
parent | f5f99929ac584126ef3f47d805dc619abc54768c (diff) |
[PATCH] IB/ipath: fix lost interrupts on HT-400
Do an extra check to see if in-memory tail changed while processing packets,
and if so, going back through the loop again (but only once per call to
ipath_kreceive()). In practice, this seems to be enough to guarantee that if
we crossed the clearing of an interrupt at start of ipath_intr with a
scheduled tail register update, that we'll process the "extra" packet that
lost the interrupt because we cleared it just as it was about to arrive.
Signed-off-by: Dave Olson <dave.olson@qlogic.com>
Signed-off-by: Bryan O'Sullivan <bryan.osullivan@qlogic.com>
Cc: "Michael S. Tsirkin" <mst@mellanox.co.il>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_driver.c | 25 | ||||
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_intr.c | 28 |
2 files changed, 36 insertions, 17 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index e2b0bc8ebaf4..66b41ecf898b 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c | |||
@@ -870,7 +870,7 @@ void ipath_kreceive(struct ipath_devdata *dd) | |||
870 | const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */ | 870 | const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */ |
871 | u32 etail = -1, l, hdrqtail; | 871 | u32 etail = -1, l, hdrqtail; |
872 | struct ips_message_header *hdr; | 872 | struct ips_message_header *hdr; |
873 | u32 eflags, i, etype, tlen, pkttot = 0, updegr=0; | 873 | u32 eflags, i, etype, tlen, pkttot = 0, updegr=0, reloop=0; |
874 | static u64 totcalls; /* stats, may eventually remove */ | 874 | static u64 totcalls; /* stats, may eventually remove */ |
875 | char emsg[128]; | 875 | char emsg[128]; |
876 | 876 | ||
@@ -885,9 +885,11 @@ void ipath_kreceive(struct ipath_devdata *dd) | |||
885 | goto bail; | 885 | goto bail; |
886 | 886 | ||
887 | l = dd->ipath_port0head; | 887 | l = dd->ipath_port0head; |
888 | if (l == (u32)le64_to_cpu(*dd->ipath_hdrqtailptr)) | 888 | hdrqtail = (u32) le64_to_cpu(*dd->ipath_hdrqtailptr); |
889 | if (l == hdrqtail) | ||
889 | goto done; | 890 | goto done; |
890 | 891 | ||
892 | reloop: | ||
891 | /* read only once at start for performance */ | 893 | /* read only once at start for performance */ |
892 | hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr); | 894 | hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr); |
893 | 895 | ||
@@ -1011,7 +1013,7 @@ void ipath_kreceive(struct ipath_devdata *dd) | |||
1011 | */ | 1013 | */ |
1012 | if (l == hdrqtail || (i && !(i&0xf))) { | 1014 | if (l == hdrqtail || (i && !(i&0xf))) { |
1013 | u64 lval; | 1015 | u64 lval; |
1014 | if (l == hdrqtail) /* want interrupt only on last */ | 1016 | if (l == hdrqtail) /* PE-800 interrupt only on last */ |
1015 | lval = dd->ipath_rhdrhead_intr_off | l; | 1017 | lval = dd->ipath_rhdrhead_intr_off | l; |
1016 | else | 1018 | else |
1017 | lval = l; | 1019 | lval = l; |
@@ -1024,6 +1026,23 @@ void ipath_kreceive(struct ipath_devdata *dd) | |||
1024 | } | 1026 | } |
1025 | } | 1027 | } |
1026 | 1028 | ||
1029 | if (!dd->ipath_rhdrhead_intr_off && !reloop) { | ||
1030 | /* HT-400 workaround; we can have a race clearing chip | ||
1031 | * interrupt with another interrupt about to be delivered, | ||
1032 | * and can clear it before it is delivered on the GPIO | ||
1033 | * workaround. By doing the extra check here for the | ||
1034 | * in-memory tail register updating while we were doing | ||
1035 | * earlier packets, we "almost" guarantee we have covered | ||
1036 | * that case. | ||
1037 | */ | ||
1038 | u32 hqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr); | ||
1039 | if (hqtail != hdrqtail) { | ||
1040 | hdrqtail = hqtail; | ||
1041 | reloop = 1; /* loop 1 extra time at most */ | ||
1042 | goto reloop; | ||
1043 | } | ||
1044 | } | ||
1045 | |||
1027 | pkttot += i; | 1046 | pkttot += i; |
1028 | 1047 | ||
1029 | dd->ipath_port0head = l; | 1048 | dd->ipath_port0head = l; |
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index bad20a395265..83b51ed89527 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c | |||
@@ -766,7 +766,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) | |||
766 | u32 istat, chk0rcv = 0; | 766 | u32 istat, chk0rcv = 0; |
767 | ipath_err_t estat = 0; | 767 | ipath_err_t estat = 0; |
768 | irqreturn_t ret; | 768 | irqreturn_t ret; |
769 | u32 p0bits, oldhead; | 769 | u32 oldhead, curtail; |
770 | static unsigned unexpected = 0; | 770 | static unsigned unexpected = 0; |
771 | static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) | | 771 | static const u32 port0rbits = (1U<<INFINIPATH_I_RCVAVAIL_SHIFT) | |
772 | (1U<<INFINIPATH_I_RCVURG_SHIFT); | 772 | (1U<<INFINIPATH_I_RCVURG_SHIFT); |
@@ -809,15 +809,16 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) | |||
809 | * lose intr for later packets that arrive while we are processing. | 809 | * lose intr for later packets that arrive while we are processing. |
810 | */ | 810 | */ |
811 | oldhead = dd->ipath_port0head; | 811 | oldhead = dd->ipath_port0head; |
812 | if (oldhead != (u32) le64_to_cpu(*dd->ipath_hdrqtailptr)) { | 812 | curtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr); |
813 | if (oldhead != curtail) { | ||
813 | if (dd->ipath_flags & IPATH_GPIO_INTR) { | 814 | if (dd->ipath_flags & IPATH_GPIO_INTR) { |
814 | ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, | 815 | ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, |
815 | (u64) (1 << 2)); | 816 | (u64) (1 << 2)); |
816 | p0bits = port0rbits | INFINIPATH_I_GPIO; | 817 | istat = port0rbits | INFINIPATH_I_GPIO; |
817 | } | 818 | } |
818 | else | 819 | else |
819 | p0bits = port0rbits; | 820 | istat = port0rbits; |
820 | ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, p0bits); | 821 | ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat); |
821 | ipath_kreceive(dd); | 822 | ipath_kreceive(dd); |
822 | if (oldhead != dd->ipath_port0head) { | 823 | if (oldhead != dd->ipath_port0head) { |
823 | ipath_stats.sps_fastrcvint++; | 824 | ipath_stats.sps_fastrcvint++; |
@@ -827,7 +828,6 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) | |||
827 | } | 828 | } |
828 | 829 | ||
829 | istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus); | 830 | istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus); |
830 | p0bits = port0rbits; | ||
831 | 831 | ||
832 | if (unlikely(!istat)) { | 832 | if (unlikely(!istat)) { |
833 | ipath_stats.sps_nullintr++; | 833 | ipath_stats.sps_nullintr++; |
@@ -890,19 +890,19 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) | |||
890 | else { | 890 | else { |
891 | /* Clear GPIO status bit 2 */ | 891 | /* Clear GPIO status bit 2 */ |
892 | ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, | 892 | ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, |
893 | (u64) (1 << 2)); | 893 | (u64) (1 << 2)); |
894 | p0bits |= INFINIPATH_I_GPIO; | ||
895 | chk0rcv = 1; | 894 | chk0rcv = 1; |
896 | } | 895 | } |
897 | } | 896 | } |
898 | chk0rcv |= istat & p0bits; | 897 | chk0rcv |= istat & port0rbits; |
899 | 898 | ||
900 | /* | 899 | /* |
901 | * clear the ones we will deal with on this round | 900 | * Clear the interrupt bits we found set, unless they are receive |
902 | * We clear it early, mostly for receive interrupts, so we | 901 | * related, in which case we already cleared them above, and don't |
903 | * know the chip will have seen this by the time we process | 902 | * want to clear them again, because we might lose an interrupt. |
904 | * the queue, and will re-interrupt if necessary. The processor | 903 | * Clear it early, so we "know" know the chip will have seen this by |
905 | * itself won't take the interrupt again until we return. | 904 | * the time we process the queue, and will re-interrupt if necessary. |
905 | * The processor itself won't take the interrupt again until we return. | ||
906 | */ | 906 | */ |
907 | ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat); | 907 | ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat); |
908 | 908 | ||