Commit 192fe71c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'parisc-for-6.4-3' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux

Pull parisc architecture fixes from Helge Deller:
 "Quite a bunch of real bugfixes in here and most of them are tagged for
  backporting: A fix for cache flushing from irq context, a kprobes &
  kgdb breakpoint handling fix, and a fix in the alternative code
  patching function to take care of CPU hotplugging.

  parisc now provides LOCKDEP support and comes with a lightweight
  spinlock check. Both features helped me to find the cache flush bug.

  Additionally writing the AGP gatt has been fixed, the machine allows
  the user to reboot after a system halt and arch_sync_dma_for_cpu() has
  been optimized for PCXL PCUs.

  Summary:

   - Fix flush_dcache_page() for usage from irq context

   - Handle kprobes breakpoints only in kernel context

   - Handle kgdb breakpoints only in kernel context

   - Use num_present_cpus() in alternative patching code

   - Enable LOCKDEP support

   - Add lightweight spinlock checks

   - Flush AGP gatt writes and adjust gatt mask in parisc_agp_mask_memory()

   - Allow to reboot machine after system halt

   - Improve cache flushing for PCXL in arch_sync_dma_for_cpu()"

* tag 'parisc-for-6.4-3' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux:
  parisc: Fix flush_dcache_page() for usage from irq context
  parisc: Handle kgdb breakpoints only in kernel context
  parisc: Handle kprobes breakpoints only in kernel context
  parisc: Allow to reboot machine after system halt
  parisc: Enable LOCKDEP support
  parisc: Add lightweight spinlock checks
  parisc: Use num_present_cpus() in alternative patching code
  parisc: Flush gatt writes and adjust gatt mask in parisc_agp_mask_memory()
  parisc: Improve cache flushing for PCXL in arch_sync_dma_for_cpu()
parents 9828ed3f 61e150fb
...@@ -130,6 +130,10 @@ config PM ...@@ -130,6 +130,10 @@ config PM
config STACKTRACE_SUPPORT config STACKTRACE_SUPPORT
def_bool y def_bool y
config LOCKDEP_SUPPORT
bool
default y
config ISA_DMA_API config ISA_DMA_API
bool bool
......
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
#
config LIGHTWEIGHT_SPINLOCK_CHECK
bool "Enable lightweight spinlock checks"
depends on SMP && !DEBUG_SPINLOCK
default y
help
Add checks with low performance impact to the spinlock functions
to catch memory overwrites at runtime. For more advanced
spinlock debugging you should choose the DEBUG_SPINLOCK option
which will detect unitialized spinlocks too.
If unsure say Y here.
...@@ -48,6 +48,10 @@ void flush_dcache_page(struct page *page); ...@@ -48,6 +48,10 @@ void flush_dcache_page(struct page *page);
#define flush_dcache_mmap_lock(mapping) xa_lock_irq(&mapping->i_pages) #define flush_dcache_mmap_lock(mapping) xa_lock_irq(&mapping->i_pages)
#define flush_dcache_mmap_unlock(mapping) xa_unlock_irq(&mapping->i_pages) #define flush_dcache_mmap_unlock(mapping) xa_unlock_irq(&mapping->i_pages)
#define flush_dcache_mmap_lock_irqsave(mapping, flags) \
xa_lock_irqsave(&mapping->i_pages, flags)
#define flush_dcache_mmap_unlock_irqrestore(mapping, flags) \
xa_unlock_irqrestore(&mapping->i_pages, flags)
#define flush_icache_page(vma,page) do { \ #define flush_icache_page(vma,page) do { \
flush_kernel_dcache_page_addr(page_address(page)); \ flush_kernel_dcache_page_addr(page_address(page)); \
......
...@@ -7,10 +7,26 @@ ...@@ -7,10 +7,26 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/spinlock_types.h> #include <asm/spinlock_types.h>
#define SPINLOCK_BREAK_INSN 0x0000c006 /* break 6,6 */
static inline void arch_spin_val_check(int lock_val)
{
if (IS_ENABLED(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK))
asm volatile( "andcm,= %0,%1,%%r0\n"
".word %2\n"
: : "r" (lock_val), "r" (__ARCH_SPIN_LOCK_UNLOCKED_VAL),
"i" (SPINLOCK_BREAK_INSN));
}
static inline int arch_spin_is_locked(arch_spinlock_t *x) static inline int arch_spin_is_locked(arch_spinlock_t *x)
{ {
volatile unsigned int *a = __ldcw_align(x); volatile unsigned int *a;
return READ_ONCE(*a) == 0; int lock_val;
a = __ldcw_align(x);
lock_val = READ_ONCE(*a);
arch_spin_val_check(lock_val);
return (lock_val == 0);
} }
static inline void arch_spin_lock(arch_spinlock_t *x) static inline void arch_spin_lock(arch_spinlock_t *x)
...@@ -18,9 +34,18 @@ static inline void arch_spin_lock(arch_spinlock_t *x) ...@@ -18,9 +34,18 @@ static inline void arch_spin_lock(arch_spinlock_t *x)
volatile unsigned int *a; volatile unsigned int *a;
a = __ldcw_align(x); a = __ldcw_align(x);
while (__ldcw(a) == 0) do {
int lock_val_old;
lock_val_old = __ldcw(a);
arch_spin_val_check(lock_val_old);
if (lock_val_old)
return; /* got lock */
/* wait until we should try to get lock again */
while (*a == 0) while (*a == 0)
continue; continue;
} while (1);
} }
static inline void arch_spin_unlock(arch_spinlock_t *x) static inline void arch_spin_unlock(arch_spinlock_t *x)
...@@ -29,15 +54,19 @@ static inline void arch_spin_unlock(arch_spinlock_t *x) ...@@ -29,15 +54,19 @@ static inline void arch_spin_unlock(arch_spinlock_t *x)
a = __ldcw_align(x); a = __ldcw_align(x);
/* Release with ordered store. */ /* Release with ordered store. */
__asm__ __volatile__("stw,ma %0,0(%1)" : : "r"(1), "r"(a) : "memory"); __asm__ __volatile__("stw,ma %0,0(%1)"
: : "r"(__ARCH_SPIN_LOCK_UNLOCKED_VAL), "r"(a) : "memory");
} }
static inline int arch_spin_trylock(arch_spinlock_t *x) static inline int arch_spin_trylock(arch_spinlock_t *x)
{ {
volatile unsigned int *a; volatile unsigned int *a;
int lock_val;
a = __ldcw_align(x); a = __ldcw_align(x);
return __ldcw(a) != 0; lock_val = __ldcw(a);
arch_spin_val_check(lock_val);
return lock_val != 0;
} }
/* /*
......
...@@ -2,13 +2,17 @@ ...@@ -2,13 +2,17 @@
#ifndef __ASM_SPINLOCK_TYPES_H #ifndef __ASM_SPINLOCK_TYPES_H
#define __ASM_SPINLOCK_TYPES_H #define __ASM_SPINLOCK_TYPES_H
#define __ARCH_SPIN_LOCK_UNLOCKED_VAL 0x1a46
typedef struct { typedef struct {
#ifdef CONFIG_PA20 #ifdef CONFIG_PA20
volatile unsigned int slock; volatile unsigned int slock;
# define __ARCH_SPIN_LOCK_UNLOCKED { 1 } # define __ARCH_SPIN_LOCK_UNLOCKED { __ARCH_SPIN_LOCK_UNLOCKED_VAL }
#else #else
volatile unsigned int lock[4]; volatile unsigned int lock[4];
# define __ARCH_SPIN_LOCK_UNLOCKED { { 1, 1, 1, 1 } } # define __ARCH_SPIN_LOCK_UNLOCKED \
{ { __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL, \
__ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL } }
#endif #endif
} arch_spinlock_t; } arch_spinlock_t;
......
...@@ -25,7 +25,7 @@ void __init_or_module apply_alternatives(struct alt_instr *start, ...@@ -25,7 +25,7 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
{ {
struct alt_instr *entry; struct alt_instr *entry;
int index = 0, applied = 0; int index = 0, applied = 0;
int num_cpus = num_online_cpus(); int num_cpus = num_present_cpus();
u16 cond_check; u16 cond_check;
cond_check = ALT_COND_ALWAYS | cond_check = ALT_COND_ALWAYS |
......
...@@ -399,6 +399,7 @@ void flush_dcache_page(struct page *page) ...@@ -399,6 +399,7 @@ void flush_dcache_page(struct page *page)
unsigned long offset; unsigned long offset;
unsigned long addr, old_addr = 0; unsigned long addr, old_addr = 0;
unsigned long count = 0; unsigned long count = 0;
unsigned long flags;
pgoff_t pgoff; pgoff_t pgoff;
if (mapping && !mapping_mapped(mapping)) { if (mapping && !mapping_mapped(mapping)) {
...@@ -420,7 +421,7 @@ void flush_dcache_page(struct page *page) ...@@ -420,7 +421,7 @@ void flush_dcache_page(struct page *page)
* to flush one address here for them all to become coherent * to flush one address here for them all to become coherent
* on machines that support equivalent aliasing * on machines that support equivalent aliasing
*/ */
flush_dcache_mmap_lock(mapping); flush_dcache_mmap_lock_irqsave(mapping, flags);
vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) { vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT; offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
addr = mpnt->vm_start + offset; addr = mpnt->vm_start + offset;
...@@ -460,7 +461,7 @@ void flush_dcache_page(struct page *page) ...@@ -460,7 +461,7 @@ void flush_dcache_page(struct page *page)
} }
WARN_ON(++count == 4096); WARN_ON(++count == 4096);
} }
flush_dcache_mmap_unlock(mapping); flush_dcache_mmap_unlock_irqrestore(mapping, flags);
} }
EXPORT_SYMBOL(flush_dcache_page); EXPORT_SYMBOL(flush_dcache_page);
......
...@@ -446,11 +446,27 @@ void arch_dma_free(struct device *dev, size_t size, void *vaddr, ...@@ -446,11 +446,27 @@ void arch_dma_free(struct device *dev, size_t size, void *vaddr,
void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, void arch_sync_dma_for_device(phys_addr_t paddr, size_t size,
enum dma_data_direction dir) enum dma_data_direction dir)
{ {
/*
* fdc: The data cache line is written back to memory, if and only if
* it is dirty, and then invalidated from the data cache.
*/
flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size); flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size);
} }
void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
enum dma_data_direction dir) enum dma_data_direction dir)
{ {
flush_kernel_dcache_range((unsigned long)phys_to_virt(paddr), size); unsigned long addr = (unsigned long) phys_to_virt(paddr);
switch (dir) {
case DMA_TO_DEVICE:
case DMA_BIDIRECTIONAL:
flush_kernel_dcache_range(addr, size);
return;
case DMA_FROM_DEVICE:
purge_kernel_dcache_range_asm(addr, addr + size);
return;
default:
BUG();
}
} }
...@@ -122,13 +122,18 @@ void machine_power_off(void) ...@@ -122,13 +122,18 @@ void machine_power_off(void)
/* It seems we have no way to power the system off via /* It seems we have no way to power the system off via
* software. The user has to press the button himself. */ * software. The user has to press the button himself. */
printk(KERN_EMERG "System shut down completed.\n" printk("Power off or press RETURN to reboot.\n");
"Please power this system off now.");
/* prevent soft lockup/stalled CPU messages for endless loop. */ /* prevent soft lockup/stalled CPU messages for endless loop. */
rcu_sysrq_start(); rcu_sysrq_start();
lockup_detector_soft_poweroff(); lockup_detector_soft_poweroff();
for (;;); while (1) {
/* reboot if user presses RETURN key */
if (pdc_iodc_getc() == 13) {
printk("Rebooting...\n");
machine_restart(NULL);
}
}
} }
void (*pm_power_off)(void); void (*pm_power_off)(void);
......
...@@ -47,6 +47,10 @@ ...@@ -47,6 +47,10 @@
#include <linux/kgdb.h> #include <linux/kgdb.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#if defined(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK)
#include <asm/spinlock.h>
#endif
#include "../math-emu/math-emu.h" /* for handle_fpe() */ #include "../math-emu/math-emu.h" /* for handle_fpe() */
static void parisc_show_stack(struct task_struct *task, static void parisc_show_stack(struct task_struct *task,
...@@ -291,24 +295,30 @@ static void handle_break(struct pt_regs *regs) ...@@ -291,24 +295,30 @@ static void handle_break(struct pt_regs *regs)
} }
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
if (unlikely(iir == PARISC_KPROBES_BREAK_INSN)) { if (unlikely(iir == PARISC_KPROBES_BREAK_INSN && !user_mode(regs))) {
parisc_kprobe_break_handler(regs); parisc_kprobe_break_handler(regs);
return; return;
} }
if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2)) { if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2 && !user_mode(regs))) {
parisc_kprobe_ss_handler(regs); parisc_kprobe_ss_handler(regs);
return; return;
} }
#endif #endif
#ifdef CONFIG_KGDB #ifdef CONFIG_KGDB
if (unlikely(iir == PARISC_KGDB_COMPILED_BREAK_INSN || if (unlikely((iir == PARISC_KGDB_COMPILED_BREAK_INSN ||
iir == PARISC_KGDB_BREAK_INSN)) { iir == PARISC_KGDB_BREAK_INSN)) && !user_mode(regs)) {
kgdb_handle_exception(9, SIGTRAP, 0, regs); kgdb_handle_exception(9, SIGTRAP, 0, regs);
return; return;
} }
#endif #endif
#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK
if ((iir == SPINLOCK_BREAK_INSN) && !user_mode(regs)) {
die_if_kernel("Spinlock was trashed", regs, 1);
}
#endif
if (unlikely(iir != GDB_BREAK_INSN)) if (unlikely(iir != GDB_BREAK_INSN))
parisc_printk_ratelimited(0, regs, parisc_printk_ratelimited(0, regs,
KERN_DEBUG "break %d,%d: pid=%d command='%s'\n", KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
......
...@@ -90,6 +90,9 @@ parisc_agp_tlbflush(struct agp_memory *mem) ...@@ -90,6 +90,9 @@ parisc_agp_tlbflush(struct agp_memory *mem)
{ {
struct _parisc_agp_info *info = &parisc_agp_info; struct _parisc_agp_info *info = &parisc_agp_info;
/* force fdc ops to be visible to IOMMU */
asm_io_sync();
writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM); writeq(info->gart_base | ilog2(info->gart_size), info->ioc_regs+IOC_PCOM);
readq(info->ioc_regs+IOC_PCOM); /* flush */ readq(info->ioc_regs+IOC_PCOM); /* flush */
} }
...@@ -158,6 +161,7 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type) ...@@ -158,6 +161,7 @@ parisc_agp_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
info->gatt[j] = info->gatt[j] =
parisc_agp_mask_memory(agp_bridge, parisc_agp_mask_memory(agp_bridge,
paddr, type); paddr, type);
asm_io_fdc(&info->gatt[j]);
} }
} }
...@@ -191,7 +195,16 @@ static unsigned long ...@@ -191,7 +195,16 @@ static unsigned long
parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr,
int type) int type)
{ {
return SBA_PDIR_VALID_BIT | addr; unsigned ci; /* coherent index */
dma_addr_t pa;
pa = addr & IOVP_MASK;
asm("lci 0(%1), %0" : "=r" (ci) : "r" (phys_to_virt(pa)));
pa |= (ci >> PAGE_SHIFT) & 0xff;/* move CI (8 bits) into lowest byte */
pa |= SBA_PDIR_VALID_BIT; /* set "valid" bit */
return cpu_to_le64(pa);
} }
static void static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment