s390 bug fixes for 2.6.21
cover s390 bug fixes for 2.6.21
s390 bug fixes for 2.6.21
PATCH 1/6 dasd: Work around gcc bug.
From: Peter Oberparleiter <[email protected]>
gcc incorrectly removes initialization of register 0 in dasd diag inline assembly. Use different register to work around this compiler bug.
---
drivers/s390/block/dasd_diag.c | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-)
drivers/s390/block/dasd_diag.c +5 -5
@@ -65,7 +65,7 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc* resulting condition code and DIAG return code. */ static inline int dia250 (void *iob,int cmd) { - register unsigned long reg0 asm ("0") = (unsigned long) iob; + register unsigned long reg2 asm ("2") = (unsigned long) iob; typedef union { struct dasd_diag_init_io init_io; struct dasd_diag_rw_io rw_io;
@@ -74,15 +74,15 @@ static inline int dia250(void *iob, int rc =3 ; asmvolatile ( -" diag 0,%2,0x250\n" +" diag 2,%2,0x250\n" "0: ipm %0\n" " srl %0,28\n" -" or %0,1\n" +" or %0,3\n" "1:\n" EX_TABLE(0b,1b) : "+d" (rc), "=m" (*(addr_type *) iob) - : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob) - : "1", "cc"); + : "d" (cmd), "d" (reg2), "m" (*(addr_type *) iob) + : "3", "cc"); return rc; }
PATCH 2/6 Fix TCP/UDP pseudo header checksum computation.
In reply to Heiko CarstensFrom: Heiko Carstens <[email protected]>
git commit f994aae1bd8e4813d59a2ed64d17585fe42d03fc changed the function declaration of csum_tcpudp_nofold. Argument types were changed from unsigned long to __be32 (unsigned int). Therefore we lost the implicit type conversion that zeroed the upper half of the registers that are used to pass parameters. Since the inline assembly relied on this we ended up adding random values and wrong checksums were created.
Showed only up on machines with more than 4GB since gcc produced code where the registers that are used to pass 'saddr' and 'daddr' previously contained addresses before calling this function.
Fix this by using 32 bit arithmetics and convert code to C, since gcc produces better code than these hand-optimized versions.
---
include/asm-s390/checksum.h | 59 +++++++++++--------------------------------- 1 files changed, 15 insertions(+), 44 deletions(-)
include/asm-s390/checksum.h +15 -44
@@ -121,50 +121,21 @@ csum_tcpudp_nofold(__be32 saddr, __be32 unsigned short len, unsigned short proto, __wsum sum) { -#ifndef __s390x__ - asmvolatile ( -" alr %0,%1\n" /* sum += saddr */ -" brc 12,0f\n" -" ahi %0,1\n" /* add carry */ -"0:" - :"+&d" (sum) :"d" (saddr) :"cc" ); - asmvolatile ( -" alr %0,%1\n" /* sum += daddr */ -" brc 12,1f\n" -" ahi %0,1\n" /* add carry */ -"1:" - :"+&d" (sum) :"d" (daddr) :"cc" ); - asmvolatile ( -" alr %0,%1\n" /* sum += len + proto */ -" brc 12,2f\n" -" ahi %0,1\n" /* add carry */ -"2:" - :"+&d" (sum) - :"d" (len + proto) - :"cc" ); -#else /* __s390x__ */ - asmvolatile ( -" lgfr %0,%0\n" -" algr %0,%1\n" /* sum += saddr */ -" brc 12,0f\n" -" aghi %0,1\n" /* add carry */ -"0: algr %0,%2\n" /* sum += daddr */ -" brc 12,1f\n" -" aghi %0,1\n" /* add carry */ -"1: algfr %0,%3\n" /* sum += len + proto */ -" brc 12,2f\n" -" aghi %0,1\n" /* add carry */ -"2: srlg 0,%0,32\n" -" alr %0,0\n" /* fold to 32 bits */ -" brc 12,3f\n" -" ahi %0,1\n" /* add carry */ -"3: llgfr %0,%0" - :"+&d" (sum) - :"d" (saddr),"d" (daddr), -"d" (len + proto) - :"cc" ,"0" ); -#endif /* __s390x__ */ -return sum; + __u32 csum = (__force __u32)sum; + + csum += (__force __u32 )saddr; +if (csum < (__force __u32 )saddr) + csum++; + + csum += (__force __u32 )daddr; +if (csum < (__force __u32 )daddr) + csum++; + + csum += len + proto; +if (csum < len + proto) + csum++; + +return (__force __wsum )csum; }/*
PATCH 3/6 kprobes: Align probe address.
In reply to Heiko CarstensFrom: David Wilder <[email protected]>
Running a probe on s390 with a probe address that is not 4 byte aligned results in a Kernel BUG. The problem is that the stura instruction used by swap_instruction requires the destination address to be 4 byte aligned. As stura only writes 4 bytes, aligning to the next 4 byte aligned address results in the breakpoint instruction being stored past the probe address. The fix is to align the address backward (to the previous 4 byte aligned address) and writing the two byte breakpoint instruction in the appropriate bytes.
---
arch/s390/kernel/kprobes.c | 2 +- 1 files changed, 1 insertion(+), 1 deletion(-)
arch/s390/kernel/kprobes.c +1 -1
@@ -167,7 +167,7 @@ static int __kprobes swap_instruction(vo* shall not cross any page boundaries (vmalloc area!) when writing * the new instruction. */ - addr = (u32 *)ALIGN ((unsigned long )args->ptr,4 ); + addr = (u32 *)((unsigned long )args->ptr &-4UL );if ((unsigned long )args->ptr &2 ) instr = ((*addr) &0xffff0000 ) | args->new;else
PATCH 4/6 cio: Device status validity.
In reply to Heiko CarstensFrom: Cornelia Huck <[email protected]>
Only accumulate device status field in irb if it is valid.
---
drivers/s390/cio/device_status.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletion(-)
drivers/s390/cio/device_status.c +5 -1
@@ -263,7 +263,11 @@ ccw_device_accumulate_irb(struct ccw_dev cdev_irb->scsw.cpa = irb->scsw.cpa;/* Accumulate device status, but not the device busy flag. */ cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY ; - cdev_irb->scsw.dstat |= irb->scsw.dstat; +/* dstat is not always valid. */ +if (irb->scsw.stctl & + (SCSW_STCTL_PRIM_STATUS |SCSW_STCTL_SEC_STATUS + |SCSW_STCTL_INTER_STATUS |SCSW_STCTL_ALERT_STATUS )) + cdev_irb->scsw.dstat |= irb->scsw.dstat;/* Accumulate subchannel status. */ cdev_irb->scsw.cstat |= irb->scsw.cstat;/* Copy residual count if it is valid. */
PATCH 5/6 zcrypt: Fix possible dead lock in AP bus module.
In reply to Heiko CarstensFrom: Ralph Wuerthner <[email protected]>
If a AP device is unconfigured __ap_poll_all() will call device_unregister() in software interrupt context which can cause dead locks. To fix this the device will be only marked as unconfigured and the device_unregister() call will be done later by either ap_scan_bus() or ap_queue_message() in process context.
---
drivers/s390/crypto/ap_bus.c | 26 +++++++++++++------------- 1 files changed, 13 insertions(+), 13 deletions(-)
drivers/s390/crypto/ap_bus.c +13 -13
@@ -757,10 +757,16 @@ static void ap_scan_bus(struct work_stru (void *)(unsigned long )qid, __ap_scan_bus); rc =ap_query_queue (qid, &queue_depth, &device_type); -if (dev && rc) { -put_device (dev); -device_unregister (dev); -continue ; +if (dev) { + ap_dev =to_ap_dev (dev); +spin_lock_bh (&ap_dev->lock); +if (rc || ap_dev->unregistered) { +spin_unlock_bh (&ap_dev->lock); +put_device (dev); +device_unregister (dev); +continue ; + }else +spin_unlock_bh (&ap_dev->lock); }if (dev) {put_device (dev);
@@ -994,7 +1000,7 @@ void ap_queue_message(struct ap_device * ap_dev->unregistered =1 ; }else { ap_dev->drv->receive (ap_dev, ap_msg,ERR_PTR (-ENODEV )); - rc =0 ; + rc = -ENODEV ; }spin_unlock_bh (&ap_dev->lock);if (rc == -ENODEV )
@@ -1044,18 +1050,12 @@ static void ap_poll_timeout(unsigned lon*/ static int __ap_poll_all (struct ap_device *ap_dev,unsigned long *flags) { -int rc; -spin_lock (&ap_dev->lock);if (!ap_dev->unregistered) { - rc =ap_poll_queue (ap_dev, flags); -if (rc) +if (ap_poll_queue (ap_dev, flags)) ap_dev->unregistered =1 ; - }else - rc =0 ; + }spin_unlock (&ap_dev->lock); -if (rc) -device_unregister (&ap_dev->device);return 0 ; }
PATCH 6/6 zcrypt: Fix ap_poll_requests counter in lost requests error path.
In reply to Heiko CarstensFrom: Ralph Wuerthner <[email protected]>
In the unlikely event that an AP device lost requests, don't forget to update the ap_poll_requests counter too. Same must happen in case an AP device is removed while there are still outstanding requests.
---
drivers/s390/crypto/ap_bus.c | 4 ++++ 1 files changed, 4 insertions(+)
drivers/s390/crypto/ap_bus.c +4 -0
@@ -505,6 +505,9 @@ static int ap_device_remove(struct devicspin_lock_bh (&ap_device_lock);list_del_init (&ap_dev->list);spin_unlock_bh (&ap_device_lock); +spin_lock_bh (&ap_dev->lock); +atomic_sub (ap_dev->queue_count, &ap_poll_requests); +spin_unlock_bh (&ap_dev->lock);return 0 ; }
@@ -867,6 +870,7 @@ static int ap_poll_read(struct ap_devicecase AP_RESPONSE_NO_PENDING_REPLY :if (status.queue_empty) {/* The card shouldn't forget requests but who knows. */ +atomic_sub (ap_dev->queue_count, &ap_poll_requests); ap_dev->queue_count =0 ;list_splice_init (&ap_dev->pendingq, &ap_dev->requestq); ap_dev->requestq_count += ap_dev->pendingq_count;