diff mbox series

parisc: Improve alternative live patching

Message ID 20181108220356.GA16610@ls3530.dellerweb.de (mailing list archive)
State Rejected, archived
Headers show
Series parisc: Improve alternative live patching | expand

Commit Message

Helge Deller Nov. 8, 2018, 10:03 p.m. UTC
Move the alternative implemenation coding to alternative.c, add code to
patch modules when loaded and improve internal arch spinlocks when
running UP.

Suggested-by: John David Anglin <dave.anglin@bell.net>
Signed-off-by: Helge Deller <deller@gmx.de>

Comments

John David Anglin Nov. 9, 2018, 12:11 a.m. UTC | #1
On 2018-11-08 5:03 p.m., Helge Deller wrote:
>   static inline int arch_spin_is_locked(arch_spinlock_t *x)
>   {
> -	volatile unsigned int *a = __ldcw_align(x);
> +	volatile unsigned int *a;
> +
> +	if (asm_online_cpus() == 1)
> +		return 1;
I think the return value should be 0 (unlocked) when asm_online_cpus() == 1.

Dave
Helge Deller Nov. 9, 2018, 7:25 a.m. UTC | #2
On 09.11.2018 01:11, John David Anglin wrote:
> On 2018-11-08 5:03 p.m., Helge Deller wrote:
>>   static inline int arch_spin_is_locked(arch_spinlock_t *x)
>>   {
>> -    volatile unsigned int *a = __ldcw_align(x);
>> +    volatile unsigned int *a;
>> +
>> +    if (asm_online_cpus() == 1)
>> +        return 1;

> I think the return value should be 0 (unlocked) when asm_online_cpus() == 1.

In that case, booting with 4 CPUs is working then, but when booting with only one CPU you get:

[    0.000000] Linux version 4.20.0-rc1-32bit+ (deller@ls3530) (gcc version 7.2.1 20170915 (Red Hat Cross 7.2.1-1) (GCC)) #943 SMP Fri Nov 9 08:08:13 CET 2018
[    0.000000] CPU0: thread -1, cpu 0, socket 0
[    0.000000] FP[0] enabled: Rev 1 Model 19
[    0.000000] The 32-bit Kernel has started...
[    0.000000] Kernel default page size is 4 KB. Huge pages disabled.
[    0.000000] printk: bootconsole [ttyB0] enabled
[    0.000000] Initialized PDC Console for debugging.
[    0.000000] Determining PDC firmware type: System Map.
[    0.000000] model 00005020 00000481 00000000 02020202 7794d7fe 100000f0 00000004 000000ba 000000ba
[    0.000000] vers  00000008
[    0.000000] CPUID vers 15 rev 8 (0x000001e8)
[    0.000000] capabilities 0x2
[    0.000000] model 9000/778/B160L
[    0.000000] Memory Ranges:
[    0.000000]  0) Start 0x0000000000000000 End 0x000000001fffffff Size    512 MB
[    0.000000] Total Memory: 512 MB
[    0.000000] PDT: Firmware does not provide any page deallocation information.
[    0.000000] percpu: Embedded 22 pages/cpu @(ptrval) s61328 r8192 d20592 u90112
[    0.000000] SMP: bootstrap CPU ID is 0
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 129920
[    0.000000] Kernel command line: console=ttyS0 root=/dev/sda5 cryptomgr.notests
[    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Memory: 507412K/524288K available (7140K kernel code, 1036K rwdata, 1651K rodata, 780K init, 376K bss, 16876K reserved, 0K cma-reserved)
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
[    0.000000] NR_IRQS: 96
[    0.000298] sched_clock: 32 bits at 250MHz, resolution 4ns, wraps every 8589934590ns
[    0.007222] Console: colour dummy device 128x48
[    0.008833] Calibrating delay loop... 1661.33 BogoMIPS (lpj=8306688)
[    0.232493] pid_max: default: 32768 minimum: 301
[    0.236972] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.237480] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.285070] rcu: Hierarchical SRCU implementation.
[    0.292563] smp: Bringing up secondary CPUs ...
[    0.293128] smp: Brought up 1 node, 1 CPU
[    0.306444] devtmpfs: initialized
[    0.320533] random: get_random_u32 called from bucket_table_alloc+0x1ac/0x1d4 with crng_init=0
[    0.326747] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.328413] futex hash table entries: 1024 (order: 3, 49152 bytes)
[    0.341270] NET: Registered protocol family 16
[    0.352413] EISA bus registered
[    0.354398] Searching for devices...
[    0.364975] Found devices:
[    0.366163] 1. Phantom PseudoBC GSC+ Port at 0xffc00000 [8] { 7, 0x0, 0x504, 0x00000 }
[    0.367071] 2. Dino PCI Bridge at 0xfff80000 [8/0] { 13, 0x3, 0x680, 0x0000a }
[    0.367621] 3. Merlin+ 132 Dino RS-232 at 0xfff83000 [8/0/63] { 10, 0x0, 0x022, 0x0008c }
[    0.368216] 4. Merlin L2 160 (9000/778/B160L) at 0xfff10000 [62] { 0, 0x0, 0x502, 0x00004 }
[    0.368799] 5. Memory at 0xfffbf000 [63] { 1, 0x0, 0x067, 0x00009 }
[    0.371441] CPU(s): 1 out of 1 PA7300LC (PCX-L2) at 250.000000 MHz online
[    0.378172] alternatives: applied 10030 out of 10030 patches
[    0.378648] BUG: spinlock already unlocked on CPU#0, swapper/0/1
[    0.378648]  lock: logbuf_lock+0x0/0x1c, .magic: dead4ead, .owner: swapper/0/1, .owner_cpu: 0
[    0.378648] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
[    0.378648] Hardware name: 9000/778/B160L
[    0.378648] Backtrace:
[    0.378648]  [<101c728c>] show_stack+0x48/0x5c
[    0.378648]  [<1089a3e0>] dump_stack+0x80/0x134
[    0.378648]  [<1024f734>] spin_bug+0xc4/0xec
[    0.378648]  [<1024f9e4>] do_raw_spin_unlock+0x7c/0xfc
[    0.378648]  [<108b9ed0>] _raw_spin_unlock+0x38/0x4c
[    0.378648]  [<10255de4>] vprintk_emit+0xe8/0x234
[    0.378648]  [<10256110>] vprintk_default+0x54/0x6c
[    0.378648]  [<10256f74>] vprintk_func+0x148/0x158
[    0.378648]  [<102566f8>] printk+0x54/0x6c
[    0.378648]  [<101d4354>] apply_alternatives+0xc4/0x1c4
[    0.378648]  [<10107ca8>] 0x10107ca8
[    0.378648]  [<10105e24>] 0x10105e24
[    0.378648]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
[    0.378648]  [<101017b0>] 0x101017b0
[    0.378648]  [<108b2320>] kernel_init+0x34/0x158
[    0.378648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
[    0.378648] 
[    0.379288] Whole cache flush 12773 cycles, flushing 10448896 bytes 4675 cycles
[    0.379735] Cache flush threshold set to 512 KiB
[    0.393941] Whole TLB flush 10612 cycles, Range flush 10448896 bytes 1450591 cycles
[    0.394391] Calculated TLB flush threshold 4 KiB
[    0.394677] TLB flush threshold set to 16 KiB
[    0.397494] Dino version 3.1 found at 0xfff80000
[    0.401648] ------------[ cut here ]------------
[    0.401948] kernel BUG at /home/cvs/LINUX/git-kernel/linux-2.6/mm/slab.c:2910!
[    0.402565] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
[    0.402903] Hardware name: 9000/778/B160L
[    0.403262] 
[    0.403489]      YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
[    0.404065] PSW: 00000000000001001011111100001110 Not tainted
[    0.404668] r00-03  0004bf0e 10b1d670 10369034 2f4bc900
[    0.404979] r04-07  2f401190 00000000 2f4001c0 2fb391a8
[    0.405238] r08-11  101a01a8 0000003c 1f999000 00000200
[    0.405495] r12-15  10ac7064 006080c0 2f4011ac 2f4011b4
[    0.405942] r16-19  007fffff 00000200 00000020 00000000
[    0.406450] r20-23  00000001 10b162a4 00000000 00000000
[    0.407061] r24-27  2f4bc900 10368ffc 102af2d0 10ac6670
[    0.407777] r28-31  2f40119f 000affff 2f4bc940 105a27f0
[    0.408534] sr00-03  00000000 00000000 00000000 00000000
[    0.409727] sr04-07  00000000 00000000 00000000 00000000
[    0.410404] 
[    0.410737] IASQ: 00000000 00000000 IAOQ: 10369050 10369054
[    0.411122]  IIR: 03ffe01f    ISR: 00000000  IOR: 10369000
[    0.411419]  CPU:        0   CR30: 2f4bc000 CR31: 00000000
[    0.411700]  ORIG_R28: 00000000
[    0.411962]  IAOQ[0]: get_first_slab+0x54/0xd8
[    0.412219]  IAOQ[1]: get_first_slab+0x58/0xd8
[    0.412467]  RP(r2): get_first_slab+0x38/0xd8
[    0.412720] Backtrace:
[    0.412908]  [<1036a254>] cache_alloc_refill+0x278/0x474
[    0.413186]  [<1036b570>] __kmalloc+0x210/0x22c
[    0.413445]  [<101f5580>] resource_list_create_entry+0x48/0x7c
[    0.413770]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
[    0.414083]  [<10133058>] 0x10133058
[    0.414306]  [<101d3348>] parisc_driver_probe+0x48/0x6c
[    0.414603]  [<10622734>] really_probe+0x2a8/0x3c8
[    0.414877]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
[    0.415157]  [<10622f84>] __driver_attach+0xb8/0x110
[    0.415426]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
[    0.415771]  [<10621e10>] driver_attach+0x48/0x5c
[    0.416086]  [<106216f4>] bus_add_driver+0x1c0/0x20c
[    0.416429]  [<10623ac0>] driver_register+0xec/0x12c
[    0.416761]  [<101d3520>] register_parisc_driver+0xac/0xb4
[    0.417115]  [<10133478>] 0x10133478
[    0.417362]  [<10105e64>] 0x10105e64
[    0.417633]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
[    0.417960]  [<101017b0>] 0x101017b0
[    0.418238]  [<108b2320>] kernel_init+0x34/0x158
[    0.418785]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
[    0.419181] 
[    0.419490] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
[    0.420044] Hardware name: 9000/778/B160L
[    0.420337] Backtrace:
[    0.420519]  [<101c728c>] show_stack+0x48/0x5c
[    0.420789]  [<1089a3e0>] dump_stack+0x80/0x134
[    0.421048]  [<101c7468>] die_if_kernel+0x18c/0x1e8
[    0.421330]  [<101c7984>] handle_interruption+0x2ec/0x730
[    0.421710]  [<10369050>] get_first_slab+0x54/0xd8
[    0.421990]  [<1036a254>] cache_alloc_refill+0x278/0x474
[    0.422432]  [<1036b570>] __kmalloc+0x210/0x22c
[    0.422797]  [<101f5580>] resource_list_create_entry+0x48/0x7c
[    0.423267]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
[    0.423724]  [<10133058>] 0x10133058
[    0.424023]  [<101d3348>] parisc_driver_probe+0x48/0x6c
[    0.424474]  [<10622734>] really_probe+0x2a8/0x3c8
[    0.424863]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
[    0.425303]  [<10622f84>] __driver_attach+0xb8/0x110
[    0.425716]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
[    0.426155]  [<10621e10>] driver_attach+0x48/0x5c
[    0.426553]  [<106216f4>] bus_add_driver+0x1c0/0x20c
[    0.426978]  [<10623ac0>] driver_register+0xec/0x12c
[    0.427428]  [<101d3520>] register_parisc_driver+0xac/0xb4
[    0.427843]  [<10133478>] 0x10133478
[    0.428148]  [<10105e64>] 0x10105e64
[    0.428509]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
[    0.428648]  [<101017b0>] 0x101017b0
[    0.428648]  [<108b2320>] kernel_init+0x34/0x158
[    0.428648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
[    0.428648] 
[    0.429129] ---[ end trace 1a6ae02625c0f6ff ]---
[    0.431183] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
[    0.432304] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
Helge Deller Nov. 9, 2018, 7:54 a.m. UTC | #3
On 09.11.2018 08:25, Helge Deller wrote:
> On 09.11.2018 01:11, John David Anglin wrote:
>> On 2018-11-08 5:03 p.m., Helge Deller wrote:
>>>   static inline int arch_spin_is_locked(arch_spinlock_t *x)
>>>   {
>>> -    volatile unsigned int *a = __ldcw_align(x);
>>> +    volatile unsigned int *a;
>>> +
>>> +    if (asm_online_cpus() == 1)
>>> +        return 1;
> 
>> I think the return value should be 0 (unlocked) when asm_online_cpus() == 1.
> 
> In that case, booting with 4 CPUs is working then, but when booting with only one CPU you get:

I think it's a inconsistency in Linux code.

[deller@ls3530 linux-2.6]$ grep -r assert_raw_spin_locked
include/linux/spinlock.h:#define assert_spin_locked(lock)       assert_raw_spin_locked(&(lock)->rlock)
include/linux/spinlock_api_up.h:#define assert_raw_spin_locked(lock)    do { (void)(lock); } while (0)
include/linux/spinlock_api_smp.h:#define assert_raw_spin_locked(x)      BUG_ON(!raw_spin_is_locked(x))

For UP, it's assumed the lock is held anyway, and assert_raw_spin_locked() is a NOP.
For SMP, raw_spin_is_locked() is always called and checked.
So, for our live patching we probably need to simulate that the lock is locked for asm_online_cpus() == 1
and thus we need to return 1 in arch_spin_is_locked().

Helge
 
> [    0.000000] Linux version 4.20.0-rc1-32bit+ (deller@ls3530) (gcc version 7.2.1 20170915 (Red Hat Cross 7.2.1-1) (GCC)) #943 SMP Fri Nov 9 08:08:13 CET 2018
> [    0.000000] CPU0: thread -1, cpu 0, socket 0
> [    0.000000] FP[0] enabled: Rev 1 Model 19
> [    0.000000] The 32-bit Kernel has started...
> [    0.000000] Kernel default page size is 4 KB. Huge pages disabled.
> [    0.000000] printk: bootconsole [ttyB0] enabled
> [    0.000000] Initialized PDC Console for debugging.
> [    0.000000] Determining PDC firmware type: System Map.
> [    0.000000] model 00005020 00000481 00000000 02020202 7794d7fe 100000f0 00000004 000000ba 000000ba
> [    0.000000] vers  00000008
> [    0.000000] CPUID vers 15 rev 8 (0x000001e8)
> [    0.000000] capabilities 0x2
> [    0.000000] model 9000/778/B160L
> [    0.000000] Memory Ranges:
> [    0.000000]  0) Start 0x0000000000000000 End 0x000000001fffffff Size    512 MB
> [    0.000000] Total Memory: 512 MB
> [    0.000000] PDT: Firmware does not provide any page deallocation information.
> [    0.000000] percpu: Embedded 22 pages/cpu @(ptrval) s61328 r8192 d20592 u90112
> [    0.000000] SMP: bootstrap CPU ID is 0
> [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 129920
> [    0.000000] Kernel command line: console=ttyS0 root=/dev/sda5 cryptomgr.notests
> [    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
> [    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
> [    0.000000] Memory: 507412K/524288K available (7140K kernel code, 1036K rwdata, 1651K rodata, 780K init, 376K bss, 16876K reserved, 0K cma-reserved)
> [    0.000000] rcu: Hierarchical RCU implementation.
> [    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
> [    0.000000] NR_IRQS: 96
> [    0.000298] sched_clock: 32 bits at 250MHz, resolution 4ns, wraps every 8589934590ns
> [    0.007222] Console: colour dummy device 128x48
> [    0.008833] Calibrating delay loop... 1661.33 BogoMIPS (lpj=8306688)
> [    0.232493] pid_max: default: 32768 minimum: 301
> [    0.236972] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
> [    0.237480] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
> [    0.285070] rcu: Hierarchical SRCU implementation.
> [    0.292563] smp: Bringing up secondary CPUs ...
> [    0.293128] smp: Brought up 1 node, 1 CPU
> [    0.306444] devtmpfs: initialized
> [    0.320533] random: get_random_u32 called from bucket_table_alloc+0x1ac/0x1d4 with crng_init=0
> [    0.326747] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
> [    0.328413] futex hash table entries: 1024 (order: 3, 49152 bytes)
> [    0.341270] NET: Registered protocol family 16
> [    0.352413] EISA bus registered
> [    0.354398] Searching for devices...
> [    0.364975] Found devices:
> [    0.366163] 1. Phantom PseudoBC GSC+ Port at 0xffc00000 [8] { 7, 0x0, 0x504, 0x00000 }
> [    0.367071] 2. Dino PCI Bridge at 0xfff80000 [8/0] { 13, 0x3, 0x680, 0x0000a }
> [    0.367621] 3. Merlin+ 132 Dino RS-232 at 0xfff83000 [8/0/63] { 10, 0x0, 0x022, 0x0008c }
> [    0.368216] 4. Merlin L2 160 (9000/778/B160L) at 0xfff10000 [62] { 0, 0x0, 0x502, 0x00004 }
> [    0.368799] 5. Memory at 0xfffbf000 [63] { 1, 0x0, 0x067, 0x00009 }
> [    0.371441] CPU(s): 1 out of 1 PA7300LC (PCX-L2) at 250.000000 MHz online
> [    0.378172] alternatives: applied 10030 out of 10030 patches
> [    0.378648] BUG: spinlock already unlocked on CPU#0, swapper/0/1
> [    0.378648]  lock: logbuf_lock+0x0/0x1c, .magic: dead4ead, .owner: swapper/0/1, .owner_cpu: 0
> [    0.378648] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
> [    0.378648] Hardware name: 9000/778/B160L
> [    0.378648] Backtrace:
> [    0.378648]  [<101c728c>] show_stack+0x48/0x5c
> [    0.378648]  [<1089a3e0>] dump_stack+0x80/0x134
> [    0.378648]  [<1024f734>] spin_bug+0xc4/0xec
> [    0.378648]  [<1024f9e4>] do_raw_spin_unlock+0x7c/0xfc
> [    0.378648]  [<108b9ed0>] _raw_spin_unlock+0x38/0x4c
> [    0.378648]  [<10255de4>] vprintk_emit+0xe8/0x234
> [    0.378648]  [<10256110>] vprintk_default+0x54/0x6c
> [    0.378648]  [<10256f74>] vprintk_func+0x148/0x158
> [    0.378648]  [<102566f8>] printk+0x54/0x6c
> [    0.378648]  [<101d4354>] apply_alternatives+0xc4/0x1c4
> [    0.378648]  [<10107ca8>] 0x10107ca8
> [    0.378648]  [<10105e24>] 0x10105e24
> [    0.378648]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
> [    0.378648]  [<101017b0>] 0x101017b0
> [    0.378648]  [<108b2320>] kernel_init+0x34/0x158
> [    0.378648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
> [    0.378648] 
> [    0.379288] Whole cache flush 12773 cycles, flushing 10448896 bytes 4675 cycles
> [    0.379735] Cache flush threshold set to 512 KiB
> [    0.393941] Whole TLB flush 10612 cycles, Range flush 10448896 bytes 1450591 cycles
> [    0.394391] Calculated TLB flush threshold 4 KiB
> [    0.394677] TLB flush threshold set to 16 KiB
> [    0.397494] Dino version 3.1 found at 0xfff80000
> [    0.401648] ------------[ cut here ]------------
> [    0.401948] kernel BUG at /home/cvs/LINUX/git-kernel/linux-2.6/mm/slab.c:2910!
> [    0.402565] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
> [    0.402903] Hardware name: 9000/778/B160L
> [    0.403262] 
> [    0.403489]      YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
> [    0.404065] PSW: 00000000000001001011111100001110 Not tainted
> [    0.404668] r00-03  0004bf0e 10b1d670 10369034 2f4bc900
> [    0.404979] r04-07  2f401190 00000000 2f4001c0 2fb391a8
> [    0.405238] r08-11  101a01a8 0000003c 1f999000 00000200
> [    0.405495] r12-15  10ac7064 006080c0 2f4011ac 2f4011b4
> [    0.405942] r16-19  007fffff 00000200 00000020 00000000
> [    0.406450] r20-23  00000001 10b162a4 00000000 00000000
> [    0.407061] r24-27  2f4bc900 10368ffc 102af2d0 10ac6670
> [    0.407777] r28-31  2f40119f 000affff 2f4bc940 105a27f0
> [    0.408534] sr00-03  00000000 00000000 00000000 00000000
> [    0.409727] sr04-07  00000000 00000000 00000000 00000000
> [    0.410404] 
> [    0.410737] IASQ: 00000000 00000000 IAOQ: 10369050 10369054
> [    0.411122]  IIR: 03ffe01f    ISR: 00000000  IOR: 10369000
> [    0.411419]  CPU:        0   CR30: 2f4bc000 CR31: 00000000
> [    0.411700]  ORIG_R28: 00000000
> [    0.411962]  IAOQ[0]: get_first_slab+0x54/0xd8
> [    0.412219]  IAOQ[1]: get_first_slab+0x58/0xd8
> [    0.412467]  RP(r2): get_first_slab+0x38/0xd8
> [    0.412720] Backtrace:
> [    0.412908]  [<1036a254>] cache_alloc_refill+0x278/0x474
> [    0.413186]  [<1036b570>] __kmalloc+0x210/0x22c
> [    0.413445]  [<101f5580>] resource_list_create_entry+0x48/0x7c
> [    0.413770]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
> [    0.414083]  [<10133058>] 0x10133058
> [    0.414306]  [<101d3348>] parisc_driver_probe+0x48/0x6c
> [    0.414603]  [<10622734>] really_probe+0x2a8/0x3c8
> [    0.414877]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
> [    0.415157]  [<10622f84>] __driver_attach+0xb8/0x110
> [    0.415426]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
> [    0.415771]  [<10621e10>] driver_attach+0x48/0x5c
> [    0.416086]  [<106216f4>] bus_add_driver+0x1c0/0x20c
> [    0.416429]  [<10623ac0>] driver_register+0xec/0x12c
> [    0.416761]  [<101d3520>] register_parisc_driver+0xac/0xb4
> [    0.417115]  [<10133478>] 0x10133478
> [    0.417362]  [<10105e64>] 0x10105e64
> [    0.417633]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
> [    0.417960]  [<101017b0>] 0x101017b0
> [    0.418238]  [<108b2320>] kernel_init+0x34/0x158
> [    0.418785]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
> [    0.419181] 
> [    0.419490] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
> [    0.420044] Hardware name: 9000/778/B160L
> [    0.420337] Backtrace:
> [    0.420519]  [<101c728c>] show_stack+0x48/0x5c
> [    0.420789]  [<1089a3e0>] dump_stack+0x80/0x134
> [    0.421048]  [<101c7468>] die_if_kernel+0x18c/0x1e8
> [    0.421330]  [<101c7984>] handle_interruption+0x2ec/0x730
> [    0.421710]  [<10369050>] get_first_slab+0x54/0xd8
> [    0.421990]  [<1036a254>] cache_alloc_refill+0x278/0x474
> [    0.422432]  [<1036b570>] __kmalloc+0x210/0x22c
> [    0.422797]  [<101f5580>] resource_list_create_entry+0x48/0x7c
> [    0.423267]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
> [    0.423724]  [<10133058>] 0x10133058
> [    0.424023]  [<101d3348>] parisc_driver_probe+0x48/0x6c
> [    0.424474]  [<10622734>] really_probe+0x2a8/0x3c8
> [    0.424863]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
> [    0.425303]  [<10622f84>] __driver_attach+0xb8/0x110
> [    0.425716]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
> [    0.426155]  [<10621e10>] driver_attach+0x48/0x5c
> [    0.426553]  [<106216f4>] bus_add_driver+0x1c0/0x20c
> [    0.426978]  [<10623ac0>] driver_register+0xec/0x12c
> [    0.427428]  [<101d3520>] register_parisc_driver+0xac/0xb4
> [    0.427843]  [<10133478>] 0x10133478
> [    0.428148]  [<10105e64>] 0x10105e64
> [    0.428509]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
> [    0.428648]  [<101017b0>] 0x101017b0
> [    0.428648]  [<108b2320>] kernel_init+0x34/0x158
> [    0.428648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
> [    0.428648] 
> [    0.429129] ---[ end trace 1a6ae02625c0f6ff ]---
> [    0.431183] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
> [    0.432304] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
>
John David Anglin Nov. 9, 2018, 3:43 p.m. UTC | #4
On 2018-11-09 2:54 a.m., Helge Deller wrote:
> On 09.11.2018 08:25, Helge Deller wrote:
>> On 09.11.2018 01:11, John David Anglin wrote:
>>> On 2018-11-08 5:03 p.m., Helge Deller wrote:
>>>>    static inline int arch_spin_is_locked(arch_spinlock_t *x)
>>>>    {
>>>> -    volatile unsigned int *a = __ldcw_align(x);
>>>> +    volatile unsigned int *a;
>>>> +
>>>> +    if (asm_online_cpus() == 1)
>>>> +        return 1;
>>> I think the return value should be 0 (unlocked) when asm_online_cpus() == 1.
>> In that case, booting with 4 CPUs is working then, but when booting with only one CPU you get:
> I think it's a inconsistency in Linux code.
>
> [deller@ls3530 linux-2.6]$ grep -r assert_raw_spin_locked
> include/linux/spinlock.h:#define assert_spin_locked(lock)       assert_raw_spin_locked(&(lock)->rlock)
> include/linux/spinlock_api_up.h:#define assert_raw_spin_locked(lock)    do { (void)(lock); } while (0)
> include/linux/spinlock_api_smp.h:#define assert_raw_spin_locked(x)      BUG_ON(!raw_spin_is_locked(x))
>
> For UP, it's assumed the lock is held anyway, and assert_raw_spin_locked() is a NOP.
> For SMP, raw_spin_is_locked() is always called and checked.
> So, for our live patching we probably need to simulate that the lock is locked for asm_online_cpus() == 1
> and thus we need to return 1 in arch_spin_is_locked().
You are correct.  arch_spin_is_locked() should return 1.  Except for one 
startup situation, it is only called
from debug_spin_unlock() where it is expected that the lock is held.

Dave
>   
>> [    0.000000] Linux version 4.20.0-rc1-32bit+ (deller@ls3530) (gcc version 7.2.1 20170915 (Red Hat Cross 7.2.1-1) (GCC)) #943 SMP Fri Nov 9 08:08:13 CET 2018
>> [    0.000000] CPU0: thread -1, cpu 0, socket 0
>> [    0.000000] FP[0] enabled: Rev 1 Model 19
>> [    0.000000] The 32-bit Kernel has started...
>> [    0.000000] Kernel default page size is 4 KB. Huge pages disabled.
>> [    0.000000] printk: bootconsole [ttyB0] enabled
>> [    0.000000] Initialized PDC Console for debugging.
>> [    0.000000] Determining PDC firmware type: System Map.
>> [    0.000000] model 00005020 00000481 00000000 02020202 7794d7fe 100000f0 00000004 000000ba 000000ba
>> [    0.000000] vers  00000008
>> [    0.000000] CPUID vers 15 rev 8 (0x000001e8)
>> [    0.000000] capabilities 0x2
>> [    0.000000] model 9000/778/B160L
>> [    0.000000] Memory Ranges:
>> [    0.000000]  0) Start 0x0000000000000000 End 0x000000001fffffff Size    512 MB
>> [    0.000000] Total Memory: 512 MB
>> [    0.000000] PDT: Firmware does not provide any page deallocation information.
>> [    0.000000] percpu: Embedded 22 pages/cpu @(ptrval) s61328 r8192 d20592 u90112
>> [    0.000000] SMP: bootstrap CPU ID is 0
>> [    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 129920
>> [    0.000000] Kernel command line: console=ttyS0 root=/dev/sda5 cryptomgr.notests
>> [    0.000000] Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
>> [    0.000000] Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
>> [    0.000000] Memory: 507412K/524288K available (7140K kernel code, 1036K rwdata, 1651K rodata, 780K init, 376K bss, 16876K reserved, 0K cma-reserved)
>> [    0.000000] rcu: Hierarchical RCU implementation.
>> [    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
>> [    0.000000] NR_IRQS: 96
>> [    0.000298] sched_clock: 32 bits at 250MHz, resolution 4ns, wraps every 8589934590ns
>> [    0.007222] Console: colour dummy device 128x48
>> [    0.008833] Calibrating delay loop... 1661.33 BogoMIPS (lpj=8306688)
>> [    0.232493] pid_max: default: 32768 minimum: 301
>> [    0.236972] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
>> [    0.237480] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
>> [    0.285070] rcu: Hierarchical SRCU implementation.
>> [    0.292563] smp: Bringing up secondary CPUs ...
>> [    0.293128] smp: Brought up 1 node, 1 CPU
>> [    0.306444] devtmpfs: initialized
>> [    0.320533] random: get_random_u32 called from bucket_table_alloc+0x1ac/0x1d4 with crng_init=0
>> [    0.326747] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
>> [    0.328413] futex hash table entries: 1024 (order: 3, 49152 bytes)
>> [    0.341270] NET: Registered protocol family 16
>> [    0.352413] EISA bus registered
>> [    0.354398] Searching for devices...
>> [    0.364975] Found devices:
>> [    0.366163] 1. Phantom PseudoBC GSC+ Port at 0xffc00000 [8] { 7, 0x0, 0x504, 0x00000 }
>> [    0.367071] 2. Dino PCI Bridge at 0xfff80000 [8/0] { 13, 0x3, 0x680, 0x0000a }
>> [    0.367621] 3. Merlin+ 132 Dino RS-232 at 0xfff83000 [8/0/63] { 10, 0x0, 0x022, 0x0008c }
>> [    0.368216] 4. Merlin L2 160 (9000/778/B160L) at 0xfff10000 [62] { 0, 0x0, 0x502, 0x00004 }
>> [    0.368799] 5. Memory at 0xfffbf000 [63] { 1, 0x0, 0x067, 0x00009 }
>> [    0.371441] CPU(s): 1 out of 1 PA7300LC (PCX-L2) at 250.000000 MHz online
>> [    0.378172] alternatives: applied 10030 out of 10030 patches
>> [    0.378648] BUG: spinlock already unlocked on CPU#0, swapper/0/1
>> [    0.378648]  lock: logbuf_lock+0x0/0x1c, .magic: dead4ead, .owner: swapper/0/1, .owner_cpu: 0
>> [    0.378648] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
>> [    0.378648] Hardware name: 9000/778/B160L
>> [    0.378648] Backtrace:
>> [    0.378648]  [<101c728c>] show_stack+0x48/0x5c
>> [    0.378648]  [<1089a3e0>] dump_stack+0x80/0x134
>> [    0.378648]  [<1024f734>] spin_bug+0xc4/0xec
>> [    0.378648]  [<1024f9e4>] do_raw_spin_unlock+0x7c/0xfc
>> [    0.378648]  [<108b9ed0>] _raw_spin_unlock+0x38/0x4c
>> [    0.378648]  [<10255de4>] vprintk_emit+0xe8/0x234
>> [    0.378648]  [<10256110>] vprintk_default+0x54/0x6c
>> [    0.378648]  [<10256f74>] vprintk_func+0x148/0x158
>> [    0.378648]  [<102566f8>] printk+0x54/0x6c
>> [    0.378648]  [<101d4354>] apply_alternatives+0xc4/0x1c4
>> [    0.378648]  [<10107ca8>] 0x10107ca8
>> [    0.378648]  [<10105e24>] 0x10105e24
>> [    0.378648]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
>> [    0.378648]  [<101017b0>] 0x101017b0
>> [    0.378648]  [<108b2320>] kernel_init+0x34/0x158
>> [    0.378648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
>> [    0.378648]
>> [    0.379288] Whole cache flush 12773 cycles, flushing 10448896 bytes 4675 cycles
>> [    0.379735] Cache flush threshold set to 512 KiB
>> [    0.393941] Whole TLB flush 10612 cycles, Range flush 10448896 bytes 1450591 cycles
>> [    0.394391] Calculated TLB flush threshold 4 KiB
>> [    0.394677] TLB flush threshold set to 16 KiB
>> [    0.397494] Dino version 3.1 found at 0xfff80000
>> [    0.401648] ------------[ cut here ]------------
>> [    0.401948] kernel BUG at /home/cvs/LINUX/git-kernel/linux-2.6/mm/slab.c:2910!
>> [    0.402565] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
>> [    0.402903] Hardware name: 9000/778/B160L
>> [    0.403262]
>> [    0.403489]      YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI
>> [    0.404065] PSW: 00000000000001001011111100001110 Not tainted
>> [    0.404668] r00-03  0004bf0e 10b1d670 10369034 2f4bc900
>> [    0.404979] r04-07  2f401190 00000000 2f4001c0 2fb391a8
>> [    0.405238] r08-11  101a01a8 0000003c 1f999000 00000200
>> [    0.405495] r12-15  10ac7064 006080c0 2f4011ac 2f4011b4
>> [    0.405942] r16-19  007fffff 00000200 00000020 00000000
>> [    0.406450] r20-23  00000001 10b162a4 00000000 00000000
>> [    0.407061] r24-27  2f4bc900 10368ffc 102af2d0 10ac6670
>> [    0.407777] r28-31  2f40119f 000affff 2f4bc940 105a27f0
>> [    0.408534] sr00-03  00000000 00000000 00000000 00000000
>> [    0.409727] sr04-07  00000000 00000000 00000000 00000000
>> [    0.410404]
>> [    0.410737] IASQ: 00000000 00000000 IAOQ: 10369050 10369054
>> [    0.411122]  IIR: 03ffe01f    ISR: 00000000  IOR: 10369000
>> [    0.411419]  CPU:        0   CR30: 2f4bc000 CR31: 00000000
>> [    0.411700]  ORIG_R28: 00000000
>> [    0.411962]  IAOQ[0]: get_first_slab+0x54/0xd8
>> [    0.412219]  IAOQ[1]: get_first_slab+0x58/0xd8
>> [    0.412467]  RP(r2): get_first_slab+0x38/0xd8
>> [    0.412720] Backtrace:
>> [    0.412908]  [<1036a254>] cache_alloc_refill+0x278/0x474
>> [    0.413186]  [<1036b570>] __kmalloc+0x210/0x22c
>> [    0.413445]  [<101f5580>] resource_list_create_entry+0x48/0x7c
>> [    0.413770]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
>> [    0.414083]  [<10133058>] 0x10133058
>> [    0.414306]  [<101d3348>] parisc_driver_probe+0x48/0x6c
>> [    0.414603]  [<10622734>] really_probe+0x2a8/0x3c8
>> [    0.414877]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
>> [    0.415157]  [<10622f84>] __driver_attach+0xb8/0x110
>> [    0.415426]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
>> [    0.415771]  [<10621e10>] driver_attach+0x48/0x5c
>> [    0.416086]  [<106216f4>] bus_add_driver+0x1c0/0x20c
>> [    0.416429]  [<10623ac0>] driver_register+0xec/0x12c
>> [    0.416761]  [<101d3520>] register_parisc_driver+0xac/0xb4
>> [    0.417115]  [<10133478>] 0x10133478
>> [    0.417362]  [<10105e64>] 0x10105e64
>> [    0.417633]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
>> [    0.417960]  [<101017b0>] 0x101017b0
>> [    0.418238]  [<108b2320>] kernel_init+0x34/0x158
>> [    0.418785]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
>> [    0.419181]
>> [    0.419490] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.20.0-rc1-32bit+ #943
>> [    0.420044] Hardware name: 9000/778/B160L
>> [    0.420337] Backtrace:
>> [    0.420519]  [<101c728c>] show_stack+0x48/0x5c
>> [    0.420789]  [<1089a3e0>] dump_stack+0x80/0x134
>> [    0.421048]  [<101c7468>] die_if_kernel+0x18c/0x1e8
>> [    0.421330]  [<101c7984>] handle_interruption+0x2ec/0x730
>> [    0.421710]  [<10369050>] get_first_slab+0x54/0xd8
>> [    0.421990]  [<1036a254>] cache_alloc_refill+0x278/0x474
>> [    0.422432]  [<1036b570>] __kmalloc+0x210/0x22c
>> [    0.422797]  [<101f5580>] resource_list_create_entry+0x48/0x7c
>> [    0.423267]  [<1057f2b4>] pci_add_resource_offset+0x48/0x98
>> [    0.423724]  [<10133058>] 0x10133058
>> [    0.424023]  [<101d3348>] parisc_driver_probe+0x48/0x6c
>> [    0.424474]  [<10622734>] really_probe+0x2a8/0x3c8
>> [    0.424863]  [<10622ec4>] driver_probe_device+0x4dc/0x4e4
>> [    0.425303]  [<10622f84>] __driver_attach+0xb8/0x110
>> [    0.425716]  [<1061fe48>] bus_for_each_dev+0xa0/0xd8
>> [    0.426155]  [<10621e10>] driver_attach+0x48/0x5c
>> [    0.426553]  [<106216f4>] bus_add_driver+0x1c0/0x20c
>> [    0.426978]  [<10623ac0>] driver_register+0xec/0x12c
>> [    0.427428]  [<101d3520>] register_parisc_driver+0xac/0xb4
>> [    0.427843]  [<10133478>] 0x10133478
>> [    0.428148]  [<10105e64>] 0x10105e64
>> [    0.428509]  [<101c39a8>] do_one_initcall+0xd0/0x1dc
>> [    0.428648]  [<101017b0>] 0x101017b0
>> [    0.428648]  [<108b2320>] kernel_init+0x34/0x158
>> [    0.428648]  [<101cc01c>] ret_from_kernel_thread+0x1c/0x24
>> [    0.428648]
>> [    0.429129] ---[ end trace 1a6ae02625c0f6ff ]---
>> [    0.431183] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
>> [    0.432304] ---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---
>>
>
diff mbox series

Patch

diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h
index bf485a94d0b4..a3630442111d 100644
--- a/arch/parisc/include/asm/alternative.h
+++ b/arch/parisc/include/asm/alternative.h
@@ -2,6 +2,7 @@ 
 #ifndef __ASM_PARISC_ALTERNATIVE_H
 #define __ASM_PARISC_ALTERNATIVE_H
 
+#define ALT_COND_ALWAYS		0x80	/* always replace instruction */
 #define ALT_COND_NO_SMP		0x01	/* when running UP instead of SMP */
 #define ALT_COND_NO_DCACHE	0x02	/* if system has no d-cache  */
 #define ALT_COND_NO_ICACHE	0x04	/* if system has no i-cache  */
@@ -9,6 +10,7 @@ 
 #define ALT_COND_NO_IOC_FDC	0x10	/* if I/O cache does not need flushes */
 
 #define INSN_PxTLB	0x02		/* modify pdtlb, pitlb */
+#define INSN_LDI_CPUs	0x34000000	/* ldi val,%reg */
 #define INSN_NOP	0x08000240	/* nop */
 
 #ifndef __ASSEMBLY__
@@ -26,6 +28,9 @@  struct alt_instr {
 };
 
 void set_kernel_text_rw(int enable_read_write);
+void apply_alternatives_all(void);
+void apply_alternatives(struct alt_instr *start, struct alt_instr *end,
+	const char *module_name);
 
 /* Alternative SMP implementation. */
 #define ALTERNATIVE(cond, replacement)		"!0:"	\
diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h
index 006fb939cac8..4b6eb5a702f5 100644
--- a/arch/parisc/include/asm/cache.h
+++ b/arch/parisc/include/asm/cache.h
@@ -61,6 +61,15 @@  void parisc_setup_cache_timing(void);
 			ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \
 			ALTERNATIVE(ALT_COND_NO_IOC_FDC, INSN_NOP) :: )
 
+#if CONFIG_SMP
+#define asm_online_cpus() ({ unsigned long __num;	\
+			asm volatile("ldi 0,%0"  /* 0 means yet unknown. */ \
+			ALTERNATIVE(ALT_COND_ALWAYS, INSN_LDI_CPUs) \
+			: "=r" (__num));	\
+			__num; })
+#else
+#define asm_online_cpus() (1)
+#endif
 #endif /* ! __ASSEMBLY__ */
 
 /* Classes of processor wrt: disabling space register hashing */
diff --git a/arch/parisc/include/asm/spinlock.h b/arch/parisc/include/asm/spinlock.h
index 8a63515f03bf..e70c15ca0886 100644
--- a/arch/parisc/include/asm/spinlock.h
+++ b/arch/parisc/include/asm/spinlock.h
@@ -4,12 +4,18 @@ 
 
 #include <asm/barrier.h>
 #include <asm/ldcw.h>
+#include <asm/cache.h>
 #include <asm/processor.h>
 #include <asm/spinlock_types.h>
 
 static inline int arch_spin_is_locked(arch_spinlock_t *x)
 {
-	volatile unsigned int *a = __ldcw_align(x);
+	volatile unsigned int *a;
+
+	if (asm_online_cpus() == 1)
+		return 1;
+
+	a = __ldcw_align(x);
 	return *a == 0;
 }
 
@@ -20,6 +26,9 @@  static inline void arch_spin_lock_flags(arch_spinlock_t *x,
 {
 	volatile unsigned int *a;
 
+	if (asm_online_cpus() == 1)
+		return;
+
 	a = __ldcw_align(x);
 	while (__ldcw(a) == 0)
 		while (*a == 0)
@@ -36,6 +45,9 @@  static inline void arch_spin_unlock(arch_spinlock_t *x)
 {
 	volatile unsigned int *a;
 
+	if (asm_online_cpus() == 1)
+		return;
+
 	a = __ldcw_align(x);
 	mb();
 	*a = 1;
@@ -46,6 +58,9 @@  static inline int arch_spin_trylock(arch_spinlock_t *x)
 	volatile unsigned int *a;
 	int ret;
 
+	if (asm_online_cpus() == 1)
+		return 1;
+
 	a = __ldcw_align(x);
         ret = __ldcw(a) != 0;
 
diff --git a/arch/parisc/kernel/Makefile b/arch/parisc/kernel/Makefile
index e5de34d00b1a..8e5f1ab65c68 100644
--- a/arch/parisc/kernel/Makefile
+++ b/arch/parisc/kernel/Makefile
@@ -7,7 +7,7 @@  extra-y			:= head.o vmlinux.lds
 
 obj-y	     	:= cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \
 		   pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
-		   ptrace.o hardware.o inventory.o drivers.o \
+		   ptrace.o hardware.o inventory.o drivers.o alternative.o \
 		   signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
 		   process.o processor.o pdc_cons.o pdc_chassis.o unwind.o
 
diff --git a/arch/parisc/kernel/alternative.c b/arch/parisc/kernel/alternative.c
new file mode 100644
index 000000000000..1a8383f22094
--- /dev/null
+++ b/arch/parisc/kernel/alternative.c
@@ -0,0 +1,106 @@ 
+/*
+ *    Alternative live-patching for parisc.
+ *    Copyright (C) 2018 Helge Deller <deller@gmx.de>
+ *
+ *    SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <asm/processor.h>
+#include <asm/sections.h>
+#include <asm/alternative.h>
+
+#include <linux/module.h>
+
+static int no_alternatives;
+static int __init setup_no_alternatives(char *str)
+{
+	no_alternatives = 1;
+	return 1;
+}
+__setup("no-alternatives", setup_no_alternatives);
+
+void __init_or_module apply_alternatives(struct alt_instr *start,
+		 struct alt_instr *end, const char *module_name)
+{
+	struct alt_instr *entry;
+	int index = 0, applied = 0;
+	int num_cpus = num_online_cpus();
+
+	for (entry = start; entry < end; entry++, index++) {
+
+		u32 *from, len, cond, replacement;
+
+		from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
+		len = entry->len;
+		cond = entry->cond;
+		replacement = entry->replacement;
+
+		WARN_ON(!cond);
+
+		if (cond != ALT_COND_ALWAYS && no_alternatives)
+			continue;
+
+		pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
+			index, cond, len, from, replacement);
+
+		if ((cond & ALT_COND_NO_SMP) && (num_cpus != 1))
+			continue;
+		if ((cond & ALT_COND_NO_DCACHE) && (cache_info.dc_size != 0))
+			continue;
+		if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
+			continue;
+
+		/*
+		 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
+		 * set (bit #61, big endian), we have to flush and sync every
+		 * time IO-PDIR is changed in Ike/Astro.
+		 */
+		if ((cond & ALT_COND_NO_IOC_FDC) &&
+			(boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC))
+			continue;
+
+		/* Want to replace pdtlb by a pdtlb,l instruction? */
+		if (replacement == INSN_PxTLB) {
+			replacement = *from;
+			if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */
+				replacement |= (1 << 10); /* set el bit */
+		}
+
+		/* Replace ldi num_cpus,%reg */
+		if (replacement == INSN_LDI_CPUs) {
+			replacement = *from;
+			replacement &= ~(31 << 1); /* mask out old values */
+			replacement |= (num_cpus << 1);
+		}
+
+		/*
+		 * Replace instruction with NOPs?
+		 * For long distance insert a branch instruction instead.
+		 */
+		if (replacement == INSN_NOP && len > 1)
+			replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
+
+		pr_debug("Do    %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
+			index, cond, len, from, replacement);
+
+		/* Replace instruction */
+		*from = replacement;
+		applied++;
+	}
+
+	pr_info("%s%salternatives: applied %d out of %d patches\n",
+		module_name ? : "", module_name ? " " : "",
+		applied, index);
+}
+
+
+void __init apply_alternatives_all(void)
+{
+	set_kernel_text_rw(1);
+
+	apply_alternatives((struct alt_instr *) &__alt_instructions,
+		(struct alt_instr *) &__alt_instructions_end, NULL);
+
+	set_kernel_text_rw(0);
+}
+
diff --git a/arch/parisc/kernel/module.c b/arch/parisc/kernel/module.c
index b5b3cb00f1fb..43778420614b 100644
--- a/arch/parisc/kernel/module.c
+++ b/arch/parisc/kernel/module.c
@@ -877,6 +877,8 @@  int module_finalize(const Elf_Ehdr *hdr,
 	int i;
 	unsigned long nsyms;
 	const char *strtab = NULL;
+	const Elf_Shdr *s;
+	char *secstrings;
 	Elf_Sym *newptr, *oldptr;
 	Elf_Shdr *symhdr = NULL;
 #ifdef DEBUG
@@ -948,6 +950,18 @@  int module_finalize(const Elf_Ehdr *hdr,
 	nsyms = newptr - (Elf_Sym *)symhdr->sh_addr;
 	DEBUGP("NEW num_symtab %lu\n", nsyms);
 	symhdr->sh_size = nsyms * sizeof(Elf_Sym);
+
+	/* find .altinstructions section */
+	secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+	for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) {
+		void *aseg = (void *) s->sh_addr;
+		char *secname = secstrings + s->sh_name;
+
+		if (!strcmp(".altinstructions", secname))
+			/* patch .altinstructions */
+			apply_alternatives(aseg, aseg + s->sh_size, me->name);
+	}
+
 	return 0;
 }
 
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index cd227f1cf629..2b108ee3b217 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -305,86 +305,6 @@  static int __init parisc_init_resources(void)
 	return 0;
 }
 
-static int no_alternatives __initdata;
-static int __init setup_no_alternatives(char *str)
-{
-	no_alternatives = 1;
-	return 1;
-}
-__setup("no-alternatives", setup_no_alternatives);
-
-static void __init apply_alternatives_all(void)
-{
-	struct alt_instr *entry;
-	int index = 0, applied = 0;
-
-
-	pr_info("alternatives: %spatching kernel code\n",
-		no_alternatives ? "NOT " : "");
-	if (no_alternatives)
-		return;
-
-	set_kernel_text_rw(1);
-
-	for (entry = (struct alt_instr *) &__alt_instructions;
-		entry < (struct alt_instr *) &__alt_instructions_end;
-		entry++, index++) {
-
-		u32 *from, len, cond, replacement;
-
-		from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
-		len = entry->len;
-		cond = entry->cond;
-		replacement = entry->replacement;
-
-		WARN_ON(!cond);
-		pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
-			index, cond, len, from, replacement);
-
-		if ((cond & ALT_COND_NO_SMP) && (num_online_cpus() != 1))
-			continue;
-		if ((cond & ALT_COND_NO_DCACHE) && (cache_info.dc_size != 0))
-			continue;
-		if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
-			continue;
-
-		/*
-		 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
-		 * set (bit #61, big endian), we have to flush and sync every
-		 * time IO-PDIR is changed in Ike/Astro.
-		 */
-		if ((cond & ALT_COND_NO_IOC_FDC) &&
-			(boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC))
-			continue;
-
-		/* Want to replace pdtlb by a pdtlb,l instruction? */
-		if (replacement == INSN_PxTLB) {
-			replacement = *from;
-			if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */
-				replacement |= (1 << 10); /* set el bit */
-		}
-
-		/*
-		 * Replace instruction with NOPs?
-		 * For long distance insert a branch instruction instead.
-		 */
-		if (replacement == INSN_NOP && len > 1)
-			replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
-
-		pr_debug("Do    %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
-			index, cond, len, from, replacement);
-
-		/* Replace instruction */
-		*from = replacement;
-		applied++;
-	}
-
-	pr_info("alternatives: applied %d out of %d patches\n", applied, index);
-
-	set_kernel_text_rw(0);
-}
-
-
 extern void gsc_init(void);
 extern void processor_init(void);
 extern void ccio_init(void);