From abd9a0c36028771a8f397f38bf79bfcf404f957f Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Fri, 17 May 2013 16:37:59 +0200 Subject: [PATCH 1/7] s390/dma: do not call debug_dma after free In dma_free_coherent call debug_dma_free_coherent before deallocating the memory to avoid a possible use after free. Reviewed-by: Gerald Schaefer Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/dma-mapping.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h index 9411db653bac..886ac7d4937a 100644 --- a/arch/s390/include/asm/dma-mapping.h +++ b/arch/s390/include/asm/dma-mapping.h @@ -71,8 +71,8 @@ static inline void dma_free_coherent(struct device *dev, size_t size, { struct dma_map_ops *dma_ops = get_dma_ops(dev); - dma_ops->free(dev, size, cpu_addr, dma_handle, NULL); debug_dma_free_coherent(dev, size, cpu_addr, dma_handle); + dma_ops->free(dev, size, cpu_addr, dma_handle, NULL); } #endif /* _ASM_S390_DMA_MAPPING_H */ From 576ebd74928fd60ae112b33c42b89602015fadbd Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Tue, 21 May 2013 16:08:22 +0200 Subject: [PATCH 2/7] kernel: Fix s390 absolute memory access for /dev/mem On s390 the prefix page and absolute zero pages are not correctly returned when reading /dev/mem. The reason is that the s390 asm/io.h file includes the asm-generic/io.h file which then defines xlate_dev_mem_ptr() and therefore overwrites the s390 specific version that does the correct swap operation for prefix and absolute zero pages. The problem is a regression that was introduced with git commit cd248341 (s390/pci: base support). To fix the problem add "#ifndef xlate_dev_mem_ptr" in asm-generic/io.h and "#define xlate_dev_mem_ptr" in asm/io.h. This ensures that the s390 version is used. For completeness also add the "#ifndef" construct for xlate_dev_kmem_ptr(). Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/io.h | 1 + include/asm-generic/io.h | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/arch/s390/include/asm/io.h b/arch/s390/include/asm/io.h index 379d96e2105e..fd9be010f9b2 100644 --- a/arch/s390/include/asm/io.h +++ b/arch/s390/include/asm/io.h @@ -36,6 +36,7 @@ static inline void * phys_to_virt(unsigned long address) } void *xlate_dev_mem_ptr(unsigned long phys); +#define xlate_dev_mem_ptr xlate_dev_mem_ptr void unxlate_dev_mem_ptr(unsigned long phys, void *addr); /* diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index ac9da00e9f2c..d5afe96adba6 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -343,8 +343,12 @@ extern void ioport_unmap(void __iomem *p); #endif /* CONFIG_GENERIC_IOMAP */ #endif /* CONFIG_HAS_IOPORT */ +#ifndef xlate_dev_kmem_ptr #define xlate_dev_kmem_ptr(p) p +#endif +#ifndef xlate_dev_mem_ptr #define xlate_dev_mem_ptr(p) __va(p) +#endif #ifdef CONFIG_VIRT_TO_BUS #ifndef virt_to_bus From 9acf73b7d06eaa3fbfbfafc9c835f8fe0d751eff Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 22 May 2013 10:24:37 +0200 Subject: [PATCH 3/7] s390/smp: lost IPIs on cpu hotplug IPIs might be lost when a cpu gets brought offline: When stop_machine executes its state machine there is a race window for the state STOPMACHINE_DISABLE_IRQ where the to be brought offline cpu might already have irqs disabled but a different cpu still may have irqs enabled. If the enabled cpu receives an interrupt and as a result sends an IPI to the to be offlined cpu in its bottom halve context, the IPI won't be noticed before the cpu is offline. In fact the race window is much larger since there is no guarantee when an IPI will be received. To fix this check for enqueued but not yet received IPIs in the cpu_disable() path and call the respective handlers before the cpu is marked offline. Reported-by: Juergen Doelle Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/smp.c | 45 +++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 05674b669001..4f977d0d25c2 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -428,34 +428,27 @@ void smp_stop_cpu(void) * This is the main routine where commands issued by other * cpus are handled. */ +static void smp_handle_ext_call(void) +{ + unsigned long bits; + + /* handle bit signal external calls */ + bits = xchg(&pcpu_devices[smp_processor_id()].ec_mask, 0); + if (test_bit(ec_stop_cpu, &bits)) + smp_stop_cpu(); + if (test_bit(ec_schedule, &bits)) + scheduler_ipi(); + if (test_bit(ec_call_function, &bits)) + generic_smp_call_function_interrupt(); + if (test_bit(ec_call_function_single, &bits)) + generic_smp_call_function_single_interrupt(); +} + static void do_ext_call_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { - unsigned long bits; - int cpu; - - cpu = smp_processor_id(); - if (ext_code.code == 0x1202) - inc_irq_stat(IRQEXT_EXC); - else - inc_irq_stat(IRQEXT_EMS); - /* - * handle bit signal external calls - */ - bits = xchg(&pcpu_devices[cpu].ec_mask, 0); - - if (test_bit(ec_stop_cpu, &bits)) - smp_stop_cpu(); - - if (test_bit(ec_schedule, &bits)) - scheduler_ipi(); - - if (test_bit(ec_call_function, &bits)) - generic_smp_call_function_interrupt(); - - if (test_bit(ec_call_function_single, &bits)) - generic_smp_call_function_single_interrupt(); - + inc_irq_stat(ext_code.code == 0x1202 ? IRQEXT_EXC : IRQEXT_EMS); + smp_handle_ext_call(); } void arch_send_call_function_ipi_mask(const struct cpumask *mask) @@ -760,6 +753,8 @@ int __cpu_disable(void) { unsigned long cregs[16]; + /* Handle possible pending IPIs */ + smp_handle_ext_call(); set_cpu_online(smp_processor_id(), false); /* Disable pseudo page faults on this cpu. */ pfault_fini(); From d5b4c2f4938aaebc392669111385c3cf50dd309f Mon Sep 17 00:00:00 2001 From: Chen Gang Date: Mon, 27 May 2013 17:55:13 +0800 Subject: [PATCH 4/7] arch: s390: appldata: using strncpy() and strnlen() instead of sprintf() 'buf[2]' is 2 bytes length, and sprintf() will append '\0' at the end of string "?\n", so original implementation is memory overflow. Need use strncpy() and strnlen() instead of sprintf(). Signed-off-by: Chen Gang Signed-off-by: Gerald Schaefer Signed-off-by: Martin Schwidefsky --- arch/s390/appldata/appldata_base.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index bae0f402bf2a..87a22092b68f 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -212,7 +212,9 @@ appldata_timer_handler(ctl_table *ctl, int write, return 0; } if (!write) { - len = sprintf(buf, appldata_timer_active ? "1\n" : "0\n"); + strncpy(buf, appldata_timer_active ? "1\n" : "0\n", + ARRAY_SIZE(buf)); + len = strnlen(buf, ARRAY_SIZE(buf)); if (len > *lenp) len = *lenp; if (copy_to_user(buffer, buf, len)) @@ -317,7 +319,8 @@ appldata_generic_handler(ctl_table *ctl, int write, return 0; } if (!write) { - len = sprintf(buf, ops->active ? "1\n" : "0\n"); + strncpy(buf, ops->active ? "1\n" : "0\n", ARRAY_SIZE(buf)); + len = strnlen(buf, ARRAY_SIZE(buf)); if (len > *lenp) len = *lenp; if (copy_to_user(buffer, buf, len)) { From b56433cb782d1cc7e44fc46d2ce3917fa75d2236 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 27 May 2013 16:19:55 +0200 Subject: [PATCH 5/7] s390/pgtable: Fix check for pgste/storage key handling pte_present might return true on PAGE_TYPE_NONE, even if the invalid bit is on. Modify the existing check of the pgste functions to avoid crashes. [ Martin Schwidefsky: added ptep_modify_prot_[start|commit] bits ] Reported-by: Martin Schwidefky Signed-off-by: Christian Borntraeger CC: stable@vger.kernel.org Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/pgtable.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index 0f0de30e3e3f..ac01463038f1 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -646,7 +646,7 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste) unsigned long address, bits; unsigned char skey; - if (!pte_present(*ptep)) + if (pte_val(*ptep) & _PAGE_INVALID) return pgste; address = pte_val(*ptep) & PAGE_MASK; skey = page_get_storage_key(address); @@ -680,7 +680,7 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste) #ifdef CONFIG_PGSTE int young; - if (!pte_present(*ptep)) + if (pte_val(*ptep) & _PAGE_INVALID) return pgste; /* Get referenced bit from storage key */ young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK); @@ -706,7 +706,7 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry) unsigned long address; unsigned long okey, nkey; - if (!pte_present(entry)) + if (pte_val(entry) & _PAGE_INVALID) return; address = pte_val(entry) & PAGE_MASK; okey = nkey = page_get_storage_key(address); @@ -1098,6 +1098,9 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm, pte = *ptep; if (!mm_exclusive(mm)) __ptep_ipte(address, ptep); + + if (mm_has_pgste(mm)) + pgste = pgste_update_all(&pte, pgste); return pte; } @@ -1105,9 +1108,13 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm, unsigned long address, pte_t *ptep, pte_t pte) { + pgste_t pgste; + if (mm_has_pgste(mm)) { + pgste = *(pgste_t *)(ptep + PTRS_PER_PTE); + pgste_set_key(ptep, pgste, pte); pgste_set_pte(ptep, pte); - pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE)); + pgste_set_unlock(ptep, pgste); } else *ptep = pte; } From 8b811bae69cf30e0a9676d7dcafb0cf16f13b3bc Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Tue, 28 May 2013 15:26:06 +0200 Subject: [PATCH 6/7] s390/dasd: fix handling of gone paths When a path is gone and dasd_generic_path_event is called with a PE_PATH_GONE event, we must assume that any I/O request on that subchannel is still running. This is unlike the dasd_generic_notify handler and the CIO_NO_PATH event, which implies that the subchannel has been cleared. If dasd_generic_path_event finds that the path has been the last usable path, it must not call dasd_generic_last_path_gone (which would reset the state of running requests), but just set the DASD_STOPPED_DC_WAIT bit. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4361d9772c42..d72a9216ee2e 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -3440,8 +3440,16 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) device->path_data.opm &= ~eventlpm; device->path_data.ppm &= ~eventlpm; device->path_data.npm &= ~eventlpm; - if (oldopm && !device->path_data.opm) - dasd_generic_last_path_gone(device); + if (oldopm && !device->path_data.opm) { + dev_warn(&device->cdev->dev, + "No verified channel paths remain " + "for the device\n"); + DBF_DEV_EVENT(DBF_WARNING, device, + "%s", "last verified path gone"); + dasd_eer_write(device, NULL, DASD_EER_NOPATH); + dasd_device_set_stop_bits(device, + DASD_STOPPED_DC_WAIT); + } } if (path_event[chp] & PE_PATH_AVAILABLE) { device->path_data.opm &= ~eventlpm; From e86cbd8765bd2e1f9eeb209822449c9b1e5958cf Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 29 May 2013 13:08:39 +0200 Subject: [PATCH 7/7] s390/pgtable: Fix gmap notifier address The address of the gmap notifier was broken, resulting in unhandled validity intercepts in KVM. Fix the rmap->vmaddr to be on a segment boundary. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- arch/s390/mm/pgtable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 18dc417aaf79..a938b548f07e 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -492,7 +492,7 @@ static int gmap_connect_pgtable(unsigned long address, unsigned long segment, mp = (struct gmap_pgtable *) page->index; rmap->gmap = gmap; rmap->entry = segment_ptr; - rmap->vmaddr = address; + rmap->vmaddr = address & PMD_MASK; spin_lock(&mm->page_table_lock); if (*segment_ptr == segment) { list_add(&rmap->list, &mp->mapper);