From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:36
Here's my current patch series that grew from implementing suspend to disk
for my powermac to a general suspend code cleanup.
One of the nice patches in this series allows you to finally suspend
via
echo mem > /sys/power/state
on powerbooks!
I've tested this whole patchset on my powerbook as well as my powermac
(which can with it suspend for the first time) and it all works well.
I expect that you will be finding problems with this, but I hope to
get this included some time in the not-too-distant future.
This patchset depends on the patches
* snd-aoa: fix onyx resume
* windfarm: don't die on suspend thread signal
to work, but will probably not apply unless you also have
* powermac: clean up PIC initialisation code
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:37
This adds mpic to the system devices and implements suspend
and resume for them. This is necessary to get interrupts for
modules back to where they were before a suspend to disk.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Without this patch, i2sbus (built as a module) for example
doesn't work properly across a suspend-to-disk/resume cycle,
with this patch it works fine.
I'm not entirely sure if there should be some wait logic
after I restore the MPIC registers, it works as-is for me.
Over the previous version of this patch this changes that
it also restores the HT interrupts if necessary, making my
quad G5 actually work.
@@ -354,6 +354,12 @@ static void mpic_startup_ht_interrupt(sttmp|=0x22;writel(tmp,fixup->base+4);spin_unlock_irqrestore(&mpic->fixup_lock,flags);++#ifdef CONFIG_PM+/* use the lowest bit inverted to the actual HW,+*setifthisfixupwasenabled,clearotherwise*/+mpic->save_data[source].fixup_data=tmp|1;+#endif}staticvoidmpic_shutdown_ht_interrupt(structmpic*mpic,unsignedintsource,
@@ -375,6 +381,12 @@ static void mpic_shutdown_ht_interrupt(stmp|=1;writel(tmp,fixup->base+4);spin_unlock_irqrestore(&mpic->fixup_lock,flags);++#ifdef CONFIG_PM+/* use the lowest bit inverted to the actual HW,+*setifthisfixupwasenabled,clearotherwise*/+mpic->save_data[source].fixup_data=tmp&~1;+#endif}staticvoid__initmpic_scan_ht_pic(structmpic*mpic,u8__iomem*devbase,
@@ -1123,7 +1135,7 @@ void __init mpic_init(struct mpic *mpic)/* Do the HT PIC fixups on U3 broken mpic */DBG("MPIC flags: %x\n",mpic->flags);if((mpic->flags&MPIC_BROKEN_U3)&&(mpic->flags&MPIC_PRIMARY))-mpic_scan_ht_pics(mpic);+mpic_scan_ht_pics(mpic);for(i=0;i<mpic->num_sources;i++){/* start with vector = source number, and masked */
@@ -1147,6 +1159,12 @@ void __init mpic_init(struct mpic *mpic)/* Set current processor priority to 0 */mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI),0);++#ifdef CONFIG_PM+/* allocate memory to save mpic state */+mpic->save_data=alloc_bootmem(mpic->num_sources*sizeof(structmpic_irq_save));+BUG_ON(mpic->save_data==NULL);+#endif}void__initmpic_set_clk_ratio(structmpic*mpic,u32clock_ratio)
@@ -1376,3 +1394,79 @@ void smp_mpic_message_pass(int target, i}}#endif /* CONFIG_SMP */++#ifdef CONFIG_PM+staticintmpic_suspend(structsys_device*dev,pm_message_tstate)+{+structmpic*mpic=container_of(dev,structmpic,sysdev);+inti;++for(i=0;i<mpic->num_sources;i++){+mpic->save_data[i].vecprio=+mpic_irq_read(i,MPIC_INFO(IRQ_VECTOR_PRI));+mpic->save_data[i].dest=+mpic_irq_read(i,MPIC_INFO(IRQ_DESTINATION));+}++return0;+}++staticintmpic_resume(structsys_device*dev)+{+structmpic*mpic=container_of(dev,structmpic,sysdev);+inti;++for(i=0;i<mpic->num_sources;i++){+mpic_irq_write(i,MPIC_INFO(IRQ_VECTOR_PRI),+mpic->save_data[i].vecprio);+mpic_irq_write(i,MPIC_INFO(IRQ_DESTINATION),+mpic->save_data[i].dest);++#ifdef CONFIG_MPIC_BROKEN_U3+{+structmpic_irq_fixup*fixup=&mpic->fixups[i];++if(fixup->base){+/* we use the lowest bit in an inverted meaning */+if((mpic->save_data[i].fixup_data&1)==0)+continue;++/* Enable and configure */+writeb(0x10+2*fixup->index,fixup->base+2);++writel(mpic->save_data[i].fixup_data&~1,+fixup->base+4);+}+}+#endif+}/* end for loop */++return0;+}+#endif++staticstructsysdev_classmpic_sysclass={+#ifdef CONFIG_PM+.resume=mpic_resume,+.suspend=mpic_suspend,+#endif+set_kset_name("mpic"),+};++staticintmpic_init_sys(void)+{+structmpic*mpic=mpics;+interror,id=0;++error=sysdev_class_register(&mpic_sysclass);++while(mpic&&!error){+mpic->sysdev.cls=&mpic_sysclass;+mpic->sysdev.id=id++;+error=sysdev_register(&mpic->sysdev);+mpic=mpic->next;+}+returnerror;+}++device_initcall(mpic_init_sys);---linux-2.6-git.orig/include/asm-powerpc/mpic.h2007-02-0702:55:06.868884289+0100+++linux-2.6-git/include/asm-powerpc/mpic.h2007-02-0704:31:41.937855996+0100
@@ -243,6 +244,14 @@ struct mpic_reg_bank {#endif /* CONFIG_PPC_DCR */};+structmpic_irq_save{+u32vecprio,+dest;+#ifdef CONFIG_MPIC_BROKEN_U3+u32fixup_data;+#endif+};+/* The instance data of a given MPIC */structmpic{
@@ -302,6 +311,12 @@ struct mpic/* link */structmpic*next;++structsys_devicesysdev;++#ifdef CONFIG_PM+structmpic_irq_save*save_data;+#endif};/*--
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:38
This patch allows "hotplugging" of CPUs on G5 machines. CPUs that are
disabled are put into an idle loop with interrupts hard-disabled, to wake
them up again we kick them just like when bringing them up.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Except for the in-irq count hack I'm happy with this. I still haven't found
where the in-hard-irq count is set to 1 in the down path during suspend or
resume and other platforms do similar things so I'm inclined to leave this.
@@ -898,7 +898,7 @@ void smp_core99_cpu_die(unsigned int cpucpu_dead[cpu]=0;}-#endif+#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 *//* Core99 Macs (dual G4s and G5s) */structsmp_ops_tcore99_smp_ops={
@@ -908,8 +908,16 @@ struct smp_ops_t core99_smp_ops = {.setup_cpu=smp_core99_setup_cpu,.give_timebase=smp_core99_give_timebase,.take_timebase=smp_core99_take_timebase,-#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32)+#if defined(CONFIG_HOTPLUG_CPU)+# if defined(CONFIG_PPC32).cpu_disable=smp_core99_cpu_disable,.cpu_die=smp_core99_cpu_die,+# endif+# if defined(CONFIG_PPC64)+.cpu_disable=generic_cpu_disable,+.cpu_die=generic_cpu_die,+/* intentionally do *NOT* assign cpu_enable,+*thegenericcodewillusekick_cputhen!*/+# endif#endif};---linux-2.6-git.orig/arch/powerpc/platforms/powermac/setup.c2007-02-0702:55:23.435884289+0100+++linux-2.6-git/arch/powerpc/platforms/powermac/setup.c2007-02-0702:55:27.388884289+0100
@@ -490,6 +490,9 @@ static int pmac_late_init(void)#ifdef CONFIG_SOFTWARE_SUSPENDpm_set_ops(&pmac_pm_ops);#endif /* CONFIG_SOFTWARE_SUSPEND */+/* this is udbg (which is __init) and we can later use it during+*cpuhotplug(insmp_core99_kick_cpu)*/+ppc_md.progress=NULL;return0;}
@@ -716,6 +719,39 @@ static int pmac_pci_probe_mode(struct pcreturnPCI_PROBE_NORMAL;returnPCI_PROBE_DEVTREE;}++#ifdef CONFIG_HOTPLUG_CPU+/* access per cpu vars from generic smp.c */+DECLARE_PER_CPU(int,cpu_state);++staticvoidpmac_cpu_die(void)+{+/* turn off as much as possible, we'll be+*kickedoutasthiswillonlybeinvoked+*oncore99platformsfornow...*/+hard_irq_disable();++printk(KERN_INFO"CPU#%d offline\n",smp_processor_id());+__get_cpu_var(cpu_state)=CPU_DEAD;+smp_wmb();++/* during the path that leads here preemption is disabled,+*reenableitnowsothatwhencominguppreemptcountis+*zerocorrectly*/+preempt_enable();++while(1){+ppc64_runlatch_off();+if(ppc_md.power_save){+ppc_md.power_save();+}else{+HMT_low();+HMT_very_low();+}+}+}+#endif+#endifstaticvoid__initpmac_init_irq(void)
@@ -558,6 +558,11 @@ int __devinit start_secondary(void *unuslocal_irq_enable();+/* it seems to be possible that we died from within+*aninterrupt...resetthein-irqcounts*/+task_thread_info(paca[cpu].__current)->preempt_count&=+~(SOFTIRQ_MASK|HARDIRQ_MASK);+cpu_idle();return0;}--
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:39
This implements save and restore hooks for IOMMUs and implements
it the dart iommu.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Paul Mackeras <redacted>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Olof Johansson <redacted>
---
The quick patch I posted w/o testing of course didn't work. This one does.
@@ -54,6 +54,9 @@ static unsigned long dart_tablesize;/* Virtual base address of the DART table */staticu32*dart_vbase;+#ifdef CONFIG_PM+staticu32*dart_copy;+#endif/* Mapped base address for the dart */staticunsignedint__iomem*dart;
@@ -346,6 +349,49 @@ void iommu_init_early_dart(void)pci_dma_ops=&dma_direct_ops;}+#ifdef CONFIG_PM+staticvoidiommu_dart_save(void)+{+memcpy(dart_copy,dart_vbase,2*1024*1024);+}++staticvoidiommu_dart_restore(void)+{+memcpy(dart_vbase,dart_copy,2*1024*1024);+dart_tlb_invalidate_all();+}++staticintiommu_init_late_dart(void)+{+unsignedlongi,tbase=(unsignedlong)dart_tablebase;+structpage*p;++/* this 16MB area is left unmapped because of caching+*considerations.thathappensearlysowecan'tmark+*itthere,doitnowinstead*/+for(i=0;i<(1<<24);i+=PAGE_SIZE)+SetPageNosave(virt_to_page((void*)(tbase+i)));++/* if no dart table exists then we don't need to save it */+if(!dart_tablebase)+return0;++/* For suspend we need to copy the dart contents because+*itisnotpartoftheregularmapping(seeabove)and+*thusnotsavedautomatically.Thememoryforthiscopy+*mustbeallocatedearlybecauseweneed2MB.*/+p=alloc_pages(GFP_KERNEL,21-PAGE_SHIFT);+BUG_ON(!p);+dart_copy=page_address(p);++ppc_md.iommu_save=iommu_dart_save;+ppc_md.iommu_restore=iommu_dart_restore;++return0;+}++late_initcall(iommu_init_late_dart);+#endifvoid__initalloc_dart_table(void){---linux-2.6-git.orig/include/asm-powerpc/iommu.h2007-02-0702:55:03.635884289+0100+++linux-2.6-git/include/asm-powerpc/iommu.h2007-02-0702:55:29.099884289+0100
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:40
On some powerpc architectures (notably 64-bit powermac) there is a memory
hole, for example on powermacs between 2G and 4G. Since we use the flat
memory model regardless, these pages must be marked as Nosave (for suspend
to disk.)
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <redacted>
---
This replaces swsusp-dont-copy-pages-that-arent-ram.patch and works just as
well, except that it doesn't modify the generic code.
Tested on powerbook and powermac.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:41
This patch changes /sys/power/state to not advertise any valid states
(except for disk if SOFTWARE_SUSPEND is enabled) when no pm_ops have been
set so userspace can easily discover what states should be available.
Also, because the pm ops in powermac are obviously not using them as
intended, I added documentation for it in kernel-doc format.
Reordering the fields in struct pm_ops not only makes the output of
kernel-doc make more sense but also removes a hole from the structure on
64-bit platforms.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-pm@lists.osdl.org
Cc: Rafael J. Wysocki <redacted>
Cc: Pavel Macheck <redacted>
@@ -161,7 +161,10 @@ static inline int valid_state(suspend_stif(state==PM_SUSPEND_DISK)return1;-if(pm_ops&&pm_ops->valid&&!pm_ops->valid(state))+/* all other states need lowlevel support and need to be+*validtothelowlevelimplementation,novalidcallback+*impliesthatallarevalid.*/+if(!pm_ops||(pm_ops->valid&&!pm_ops->valid(state)))return0;return1;}---linux-2.6-git.orig/include/linux/pm.h2007-02-0702:55:02.028884289+0100+++linux-2.6-git/include/linux/pm.h2007-02-0702:55:31.208884289+0100
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:42
In commit 0fba3a1f39f8b0a50b56c8b068fa52131cbc84c2 (a very long time ago,
May 2006), I fixed a bug that caused powermacs to crash when you tried
entering standby/mem suspend states.
As I'm now getting more familiar with the suspend code I notice a few
more things:
1. we previously misunderstood what pm_ops is for, it isn't supposed to be
for doing platform dependent suspend/resume stuff that needs to be done
for suspend to disk (as we currently try to use it!), it is instead for
entering platform dependent suspend states ("standby", "mem").
2. due to the first point, we never properly save FPU and altivec states
when suspending to disk. It probably hasn't hurt yet because the process
that writes the "disk" to /sys/power/state uses neither and its context
is used.
This patch addresses these points as follows:
1. remove all pm_ops from powermac, powermac suspend to ram isn't currently
usable via /sys/power/state but is done via the PMU instead.
2. move the code responsible for storing FPU/altivec state into a new
arch_prepare_suspend function (previously, this was only present for
32-bit platforms from asm-ppc.)
I removed
set_context(current->active_mm->context.id, current->active_mm->pgd);
because
1. it works without and
2. I don't know what the point is
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
@@ -420,76 +420,11 @@ static void __init find_boot_device(void#endif}-/* TODO: Merge the suspend-to-ram with the common code !!!-*currently,thisisastubimplementationforsuspend-to-disk-*only-*/--#ifdef CONFIG_SOFTWARE_SUSPEND--staticintpmac_pm_prepare(suspend_state_tstate)-{-printk(KERN_DEBUG"%s(%d)\n",__FUNCTION__,state);--return0;-}--staticintpmac_pm_enter(suspend_state_tstate)-{-printk(KERN_DEBUG"%s(%d)\n",__FUNCTION__,state);--/* Giveup the lazy FPU & vec so we don't have to back them-*upfromthelowlevelcode-*/-enable_kernel_fp();--#ifdef CONFIG_ALTIVEC-if(cur_cpu_spec->cpu_features&CPU_FTR_ALTIVEC)-enable_kernel_altivec();-#endif /* CONFIG_ALTIVEC */--return0;-}--staticintpmac_pm_finish(suspend_state_tstate)-{-printk(KERN_DEBUG"%s(%d)\n",__FUNCTION__,state);--/* Restore userland MMU context */-set_context(current->active_mm->context.id,current->active_mm->pgd);--return0;-}--staticintpmac_pm_valid(suspend_state_tstate)-{-switch(state){-casePM_SUSPEND_DISK:-return1;-/* can't do any other states via generic mechanism yet */-default:-return0;-}-}--staticstructpm_opspmac_pm_ops={-.pm_disk_mode=PM_DISK_SHUTDOWN,-.prepare=pmac_pm_prepare,-.enter=pmac_pm_enter,-.finish=pmac_pm_finish,-.valid=pmac_pm_valid,-};--#endif /* CONFIG_SOFTWARE_SUSPEND */-staticintinitializing=1;staticintpmac_late_init(void){initializing=0;-#ifdef CONFIG_SOFTWARE_SUSPEND-pm_set_ops(&pmac_pm_ops);-#endif /* CONFIG_SOFTWARE_SUSPEND *//* this is udbg (which is __init) and we can later use it during*cpuhotplug(insmp_core99_kick_cpu)*/ppc_md.progress=NULL;---/dev/null1970-01-0100:00:00.000000000+0000+++linux-2.6-git/include/asm-powerpc/suspend.h2007-02-0702:55:32.124884289+0100
@@ -0,0 +1,26 @@+/*+*Commonpowerpcsuspendcodefor32and64bits+*+*Copyright2007JohannesBerg<johannes@sipsolutions.net>+*+*GPLv2+*/++#include<asm/cputable.h>+#include<asm/system.h>+++intsave_processor_state(void)+{+/* Giveup the lazy FPU & vec so we don't have to back them+*upfromthelowlevelcode+*/+enable_kernel_fp();++#ifdef CONFIG_ALTIVEC+if(cur_cpu_spec->cpu_features&CPU_FTR_ALTIVEC)+enable_kernel_altivec();+#endif /* CONFIG_ALTIVEC */++return0;+}--
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:43
Powermac G5 suspend to disk implementation. The code is platform
agnostic but only tested on powermac, no other 64-bit powerpc
machines.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
WARNING: nvidiafb breaks suspend. I don't know why yet, but if it is
compiled and you try to suspend from X it all just freezes. The nvidiafb
suspend code looks a bit fishy but I shall have to understand first what
it's trying to achieve.
Other than that I'm really happy with this, it's good to finally be able to
hibernate the machine instead of keeping it up or shutting it down.
Tested on my powermac, this time with forced preemption enabled, of course
only together with all the other patches.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:44
The original code here is wrong, it applies "previous" knowledge.
The way the cpufreq core is designed is that the policy for the
secondary CPU that comes online says that it must in fact not
use this policy but use the same as the other CPUs that are
listed, which in fact is CPU#0.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Paul Mackeras <redacted>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: cpufreq@lists.linux.org.uk
Cc: Jacob Shin <redacted>
---
Tested on my powermac with suspend-to-disk.
Paul, please apply to powerpc.
@@ -357,13 +357,13 @@ static unsigned int g5_cpufreq_get_speedstaticintg5_cpufreq_cpu_init(structcpufreq_policy*policy){-if(policy->cpu!=0)-return-ENODEV;-policy->governor=CPUFREQ_DEFAULT_GOVERNOR;policy->cpuinfo.transition_latency=CPUFREQ_ETERNAL;policy->cur=g5_cpu_freqs[g5_query_freq()].frequency;-policy->cpus=cpu_possible_map;+/* secondary CPUs are tied to the primary one by the+*cpufreqcoreifinthesecondarypolicywetellitthat+*itactuallymustbeonepolicytogetherwithallothers.*/+policy->cpus=cpu_online_map;cpufreq_frequency_table_get_attr(g5_cpu_freqs,policy->cpu);returncpufreq_frequency_table_cpuinfo(policy,--
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:45
arch/powerpc/mm/mem.c states that page_is_ram is called by the code that
implements /dev/mem which isn't true. Remove the comment.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Paul Mackerras <redacted>
---
Please apply to the powerpc tree.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:46
arch/powerpc/mm/mem.c exports page_is_ram and phys_mem_access_prot which
both are not used anywhere that could be modular.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Paul Mackerras <redacted>
---
I don't see any possible way these could be used by external modules either,
especially since they are not portable across arches.
@@ -83,7 +83,6 @@ int page_is_ram(unsigned long pfn)return0;#endif}-EXPORT_SYMBOL(page_is_ram);pgprot_tphys_mem_access_prot(structfile*file,unsignedlongpfn,unsignedlongsize,pgprot_tvma_prot)
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:47
Tracing through the code, no current PMU sleep notifier can abort sleep.
Since no new PMU sleep notifiers should be added, this patch simplifies the
code and removes the ability to abort sleep.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
This is just something I did before I understood how to do the next patch
in this series. If you want, I can drop this one and rework the next, it's
very well possible to abort suspend from these callbacks even with the
next patch.
@@ -391,13 +374,8 @@ do_adb_reset_bus(void)if(adb_controller->autopoll)adb_controller->autopoll(0);-nret=blocking_notifier_call_chain(&adb_client_list,-ADB_MSG_PRE_RESET,NULL);-if(nret&NOTIFY_STOP_MASK){-if(adb_controller->autopoll)-adb_controller->autopoll(autopoll_devs);-return-EBUSY;-}+blocking_notifier_call_chain(&adb_client_list,+ADB_MSG_PRE_RESET,NULL);if(sleepy_trackpad){/* Let the trackpad settle down */
@@ -2014,12 +1999,8 @@ pmac_suspend_devices(void)pm_prepare_console();-/* Notify old-style device drivers & userland */-ret=broadcast_sleep(PBOOK_SLEEP_REQUEST,PBOOK_SLEEP_REJECT);-if(ret!=PBOOK_SLEEP_OK){-printk(KERN_ERR"Sleep rejected by drivers\n");-return-EBUSY;-}+/* Notify old-style device drivers */+broadcast_sleep(PBOOK_SLEEP_REQUEST);/* Sync the disks. *//* XXX It would be nice to have some way to ensure that
@@ -2029,12 +2010,7 @@ pmac_suspend_devices(void)*/sys_sync();-/* Sleep can fail now. May not be very robust but useful for debugging */-ret=broadcast_sleep(PBOOK_SLEEP_NOW,PBOOK_WAKE);-if(ret!=PBOOK_SLEEP_OK){-printk(KERN_ERR"Driver sleep failed\n");-return-EBUSY;-}+broadcast_sleep(PBOOK_SLEEP_NOW);/* Send suspend call to devices, hold the device core's dpm_sem */ret=device_suspend(PMSG_SUSPEND);---linux-2.6-git.orig/include/linux/pmu.h2007-02-0702:54:56.728884289+0100+++linux-2.6-git/include/linux/pmu.h2007-02-0702:55:35.968884289+0100
@@ -168,24 +168,16 @@ extern int pmu_get_model(void);structpmu_sleep_notifier{-int(*notifier_call)(structpmu_sleep_notifier*self,intwhen);+void(*notifier_call)(structpmu_sleep_notifier*self,intwhen);intpriority;structlist_headlist;};/* Code values for calling sleep/wakeup handlers-*-*Note:Ifasleeprequestgotcancelled,alldriverswillget-*thePBOOK_SLEEP_REJECT,eventhosewhodidn'tgetthePBOOK_SLEEP_REQUEST.*/#define PBOOK_SLEEP_REQUEST 1#define PBOOK_SLEEP_NOW 2-#define PBOOK_SLEEP_REJECT 3-#define PBOOK_WAKE 4--/* Result codes returned by the notifiers */-#define PBOOK_SLEEP_OK 0-#define PBOOK_SLEEP_REFUSE -1+#define PBOOK_WAKE 3/* priority levels in notifiers */#define SLEEP_LEVEL_VIDEO 100 /* Video driver (first wake) */---linux-2.6-git.orig/drivers/macintosh/via-pmu-led.c2007-02-0702:54:56.425884289+0100+++linux-2.6-git/drivers/macintosh/via-pmu-led.c2007-02-0702:55:35.969884289+0100
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-07 12:45:48
After having removed the power management ops from powermac completely, this
patch adds them back for PMU based machines, directly in the PMU driver.
This finally allows suspending via /sys/power/state on powerbooks.
The patch also replaces the PMU ioctl with a simple call to
pm_suspend(PM_SUSPEND_MEM) and puts the PMU ioctls onto the feature-removal
schedule.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
Tested on my powerbook and it works! Needs some more testing on older
powerbooks I suppose, just to see if they get problems with the slight
reordering of the suspend/resume sequence.
And before someone asks:
Yes, it is safe to remove the backlight ioctl restrictions
because the generic layer actually freezes processes before STR.
diffstat:
Documentation/feature-removal-schedule.txt | 8
drivers/macintosh/via-pmu.c | 306 +++++++++++------------------
2 files changed, 134 insertions(+), 180 deletions(-)
@@ -1992,132 +1989,6 @@ restore_via_state(void)externvoidpmu_backlight_set_sleep(intsleep);-staticint-pmac_suspend_devices(void)-{-intret;--pm_prepare_console();--/* Notify old-style device drivers */-broadcast_sleep(PBOOK_SLEEP_REQUEST);--/* Sync the disks. */-/* XXX It would be nice to have some way to ensure that-*nobodyisdirtyinganynewbufferswhilewewait.That-*couldbeachievedusingtherefrigeratorforprocesses-*thatswsuspuses-*/-sys_sync();--broadcast_sleep(PBOOK_SLEEP_NOW);--/* Send suspend call to devices, hold the device core's dpm_sem */-ret=device_suspend(PMSG_SUSPEND);-if(ret){-broadcast_wake();-printk(KERN_ERR"Driver sleep failed\n");-return-EBUSY;-}--#ifdef CONFIG_PMAC_BACKLIGHT-/* Tell backlight code not to muck around with the chip anymore */-pmu_backlight_set_sleep(1);-#endif--/* Call platform functions marked "on sleep" */-pmac_pfunc_i2c_suspend();-pmac_pfunc_base_suspend();--/* Stop preemption */-preempt_disable();--/* Make sure the decrementer won't interrupt us */-asmvolatile("mtdec %0"::"r"(0x7fffffff));-/* Make sure any pending DEC interrupt occurring while we did-*theabovedidn'tre-enabletheDEC*/-mb();-asmvolatile("mtdec %0"::"r"(0x7fffffff));--/* We can now disable MSR_EE. This code of course works properly only-*onUPmachines...ForSMP,ifweeverimplementsleep,we'llhaveto-*stopthe"other"CPUswaybeforewedoallthatstuff.-*/-local_irq_disable();--/* Broadcast power down irq-*Thisisn'tthatusefulinmostcases(onlydirectlywireddevicescan-*usethisbutstill...Thiswilltakecareofsysdev'saswell,so-*weexitfromherewithlocalirqsdisabledandPICoff.-*/-ret=device_power_down(PMSG_SUSPEND);-if(ret){-wakeup_decrementer();-local_irq_enable();-preempt_enable();-device_resume();-broadcast_wake();-printk(KERN_ERR"Driver powerdown failed\n");-return-EBUSY;-}--/* Wait for completion of async requests */-while(!batt_req.complete)-pmu_poll();--/* Giveup the lazy FPU & vec so we don't have to back them-*upfromthelowlevelcode-*/-enable_kernel_fp();--#ifdef CONFIG_ALTIVEC-if(cpu_has_feature(CPU_FTR_ALTIVEC))-enable_kernel_altivec();-#endif /* CONFIG_ALTIVEC */--return0;-}--staticint-pmac_wakeup_devices(void)-{-mdelay(100);--#ifdef CONFIG_PMAC_BACKLIGHT-/* Tell backlight code it can use the chip again */-pmu_backlight_set_sleep(0);-#endif--/* Power back up system devices (including the PIC) */-device_power_up();--/* Force a poll of ADB interrupts */-adb_int_pending=1;-via_pmu_interrupt(0,NULL);--/* Restart jiffies & scheduling */-wakeup_decrementer();--/* Re-enable local CPU interrupts */-local_irq_enable();-mdelay(10);-preempt_enable();--/* Call platform functions marked "on wake" */-pmac_pfunc_base_resume();-pmac_pfunc_i2c_resume();--/* Resume devices */-device_resume();--/* Notify old style drivers */-broadcast_wake();--pm_restore_console();--return0;-}-#define GRACKLE_PM (1<<7)#define GRACKLE_DOZE (1<<5)#define GRACKLE_NAP (1<<4)
@@ -2128,19 +1999,12 @@ static int powerbook_sleep_grackle(void)unsignedlongsave_l2cr;unsignedshortpmcr1;structadb_requestreq;-intret;structpci_dev*grackle;grackle=pci_find_slot(0,0);if(!grackle)return-ENODEV;-ret=pmac_suspend_devices();-if(ret){-printk(KERN_ERR"Sleep rejected by devices\n");-returnret;-}-/* Turn off various things. Darwin does some retry tests here... */pmu_request(&req,NULL,2,PMU_POWER_CTRL0,PMU_POW0_OFF|PMU_POW0_HARD_DRIVE);pmu_wait_complete(&req);
@@ -2201,8 +2065,6 @@ static int powerbook_sleep_grackle(void)PMU_POW_ON|PMU_POW_BACKLIGHT|PMU_POW_CHARGER|PMU_POW_IRLED|PMU_POW_MEDIABAY);pmu_wait_complete(&req);-pmac_wakeup_devices();-return0;}
@@ -2212,7 +2074,6 @@ powerbook_sleep_Core99(void)unsignedlongsave_l2cr;unsignedlongsave_l3cr;structadb_requestreq;-intret;if(pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1)<0){printk(KERN_ERR"Sleep mode not supported on this machine\n");
@@ -2222,12 +2083,6 @@ powerbook_sleep_Core99(void)if(num_online_cpus()>1||cpu_is_offline(0))return-EAGAIN;-ret=pmac_suspend_devices();-if(ret){-printk(KERN_ERR"Sleep rejected by devices\n");-returnret;-}-/* Stop environment and ADB interrupts */pmu_request(&req,NULL,2,PMU_SET_INTR_MASK,0);pmu_wait_complete(&req);
@@ -2298,8 +2153,6 @@ powerbook_sleep_Core99(void)/* Restore LPJ, cpufreq will adjust the cpu frequency */loops_per_jiffy/=2;-pmac_wakeup_devices();-return0;}
@@ -2327,13 +2180,6 @@ powerbook_sleep_3400(void)/* Allocate room for PCI save */pbook_alloc_pci_save();-ret=pmac_suspend_devices();-if(ret){-pbook_free_pci_save();-printk(KERN_ERR"Sleep rejected by devices\n");-returnret;-}-/* Save the state of PCI config space for some slots */pbook_pci_save();
@@ -2559,6 +2404,126 @@ pmu_release(struct inode *inode, struct return0;}+#if defined(CONFIG_PM) && defined(CONFIG_PPC32)+staticintpowerbook_prepare_sleep(suspend_state_tstate)+{+/* Notify old-style device drivers */+broadcast_sleep(PBOOK_SLEEP_REQUEST);++sys_sync();++broadcast_sleep(PBOOK_SLEEP_NOW);++#ifdef CONFIG_PMAC_BACKLIGHT+/* Tell backlight code not to muck around with the chip anymore */+pmu_backlight_set_sleep(1);+#endif++/* Call platform functions marked "on sleep" */+pmac_pfunc_i2c_suspend();+pmac_pfunc_base_suspend();++preempt_disable();++return0;+}++staticintpowerbook_sleep(suspend_state_tstate)+{+interror=0;++asmvolatile("mtdec %0"::"r"(0x7fffffff));+/* Make sure any pending DEC interrupt occurring while we did+*theabovedidn'tre-enabletheDEC*/+mb();+asmvolatile("mtdec %0"::"r"(0x7fffffff));++/* Wait for completion of async requests */+while(!batt_req.complete)+pmu_poll();++/* Giveup the lazy FPU & vec so we don't have to back them+*upfromthelowlevelcode+*/+enable_kernel_fp();++#ifdef CONFIG_ALTIVEC+if(cpu_has_feature(CPU_FTR_ALTIVEC))+enable_kernel_altivec();+#endif /* CONFIG_ALTIVEC */++switch(pmu_kind){+casePMU_OHARE_BASED:+error=powerbook_sleep_3400();+break;+casePMU_HEATHROW_BASED:+casePMU_PADDINGTON_BASED:+error=powerbook_sleep_grackle();+break;+casePMU_KEYLARGO_BASED:+error=powerbook_sleep_Core99();+break;+default:+return-ENOSYS;+}++if(error)+returnerror;++mdelay(100);++/* Force a poll of ADB interrupts */+adb_int_pending=1;+via_pmu_interrupt(0,NULL);++/* Restart jiffies & scheduling */+wakeup_decrementer();++return0;+}++staticintpowerbook_finish_sleep(suspend_state_tstate)+{+#ifdef CONFIG_PMAC_BACKLIGHT+/* Tell backlight code it can use the chip again */+pmu_backlight_set_sleep(0);+#endif++preempt_enable();++/* Call platform functions marked "on wake" */+pmac_pfunc_base_resume();+pmac_pfunc_i2c_resume();++/* Notify old style drivers */+broadcast_wake();++return0;+}++staticintpmu_sleep_valid(suspend_state_tstate)+{+returnstate==PM_SUSPEND_MEM;+}++staticstructpm_opspmu_pm_ops={+.pm_disk_mode=PM_DISK_PLATFORM,+.prepare=powerbook_prepare_sleep,+.finish=powerbook_finish_sleep,+.enter=powerbook_sleep,+.valid=pmu_sleep_valid,+};++staticintregister_pmu_pm_ops(void)+{+pm_set_ops(&pmu_pm_ops);++return0;+}++device_initcall(register_pmu_pm_ops);+#endif+staticintpmu_ioctl(structinode*inode,structfile*filp,u_intcmd,u_longarg)
@@ -2568,27 +2533,14 @@ pmu_ioctl(struct inode * inode, struct fswitch(cmd){#if defined(CONFIG_PM) && defined(CONFIG_PPC32)+/* just provided for compatibility */casePMU_IOC_SLEEP:if(!capable(CAP_SYS_ADMIN))return-EACCES;-if(sleep_in_progress)-return-EBUSY;-sleep_in_progress=1;-switch(pmu_kind){-casePMU_OHARE_BASED:-error=powerbook_sleep_3400();-break;-casePMU_HEATHROW_BASED:-casePMU_PADDINGTON_BASED:-error=powerbook_sleep_grackle();-break;-casePMU_KEYLARGO_BASED:-error=powerbook_sleep_Core99();-break;-default:-error=-ENOSYS;-}-sleep_in_progress=0;+printk(KERN_INFO"via-pmu: suspending via ioctl is deprecated.\n");+printk(KERN_INFO"via-pmu: use \"echo mem > /sys/power/state\" instead!\n");+printk(KERN_INFO"via-pmu: this ioctl will be removed soon.\n");+error=pm_suspend(PM_SUSPEND_MEM);break;casePMU_IOC_CAN_SLEEP:if(pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1)<0)
@@ -325,3 +325,11 @@ Why: Unmaintained for years, superceded Who:JeffGarzik<jeff@garzik.org>---------------------------++What:via-pmususpendioctls(PMU_IOC_SLEEP,PMU_IOC_CAN_SLEEP)+When:February2008+Why:Theregularplatform-independentinterfacein/sys/power/state+shouldbeusedinstead.+Who:JohannesBerg<johannes@sipsolutions.net>++-----------------------------
From: Andreas Schwab <hidden> Date: 2007-02-07 22:52:50
Johannes Berg [off-list ref] writes:
Tracing through the code, no current PMU sleep notifier can abort sleep.
Since no new PMU sleep notifiers should be added, this patch simplifies the
code and removes the ability to abort sleep.
There is a use of PBOOK_SLEEP_OK left in
arch/powerpc/platforms/powermac/time.c.
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:08:13
On Wed, 2007-02-07 at 23:52 +0100, Andreas Schwab wrote:
Johannes Berg [off-list ref] writes:
quoted
Tracing through the code, no current PMU sleep notifier can abort sleep.
Since no new PMU sleep notifiers should be added, this patch simplifies the
code and removes the ability to abort sleep.
There is a use of PBOOK_SLEEP_OK left in
arch/powerpc/platforms/powermac/time.c.
Ouch. Yeah, the patch was done assuming my time resume patch is applied.
I found another one though, in dmasound.
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:10:53
On Thu, 2007-02-08 at 15:25 +1100, Paul Mackerras wrote:
Johannes Berg writes:
quoted
arch/powerpc/mm/mem.c exports page_is_ram and phys_mem_access_prot which
both are not used anywhere that could be modular.
phys_mem_access_prot is used in drivers/video/fbmem.c, and the fb
stuff can be a module.
Ouch, sorry, the Makefile there with fb-y confused me. Seems all these
patches have one problem or another :(
I'll repost just the removal of page_is_ram then.
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:14:54
On Wed, 2007-02-07 at 13:45 +0100, Johannes Berg wrote:
Except for the in-irq count hack I'm happy with this. I still haven't found
where the in-hard-irq count is set to 1 in the down path during suspend or
resume and other platforms do similar things so I'm inclined to leave this.
Um, ok, so the hack breaks platforms that don't have paca, e.g. chrp32.
Also, I finally figured out how the in-hard-irq count happens. The thing
is that when I try to turn off the CPU it actually doesn't really turn
off of course, so it ends up doing NAP and taking timer interrupts...
which goes irq_enter() and we happen to kill it afterwards.
I have two ways of fixing this:
- just ignore it as we do now
- insert a "if (cpu_dead) return" into the timer interrupt function
I prefer the latter because then we're guaranteed that whatever the
timer interrupt does we don't modify any state for/by the CPU that isn't
supposed to exist.
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:16:26
Tracing through the code, no current PMU sleep notifier can abort sleep.
Since no new PMU sleep notifiers should be added, this patch simplifies the
code and removes the ability to abort sleep.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
This is just something I did before I understood how to do the next patch
in this series. If you want, I can drop this one and rework the next, it's
very well possible to abort suspend from these callbacks even with the
next patch.
Previous version missed dmasound. I'd completely forgotten that still
exists. Maybe we should just remove it?
As Andreas pointed out, it doesn't change the one in
arch/powerpc/kernel/time.c, but that has been removed by the previous
time suspend patch so this patch depends on that.
@@ -391,13 +374,8 @@ do_adb_reset_bus(void)if(adb_controller->autopoll)adb_controller->autopoll(0);-nret=blocking_notifier_call_chain(&adb_client_list,-ADB_MSG_PRE_RESET,NULL);-if(nret&NOTIFY_STOP_MASK){-if(adb_controller->autopoll)-adb_controller->autopoll(autopoll_devs);-return-EBUSY;-}+blocking_notifier_call_chain(&adb_client_list,+ADB_MSG_PRE_RESET,NULL);if(sleepy_trackpad){/* Let the trackpad settle down */
@@ -2014,12 +1999,8 @@ pmac_suspend_devices(void)pm_prepare_console();-/* Notify old-style device drivers & userland */-ret=broadcast_sleep(PBOOK_SLEEP_REQUEST,PBOOK_SLEEP_REJECT);-if(ret!=PBOOK_SLEEP_OK){-printk(KERN_ERR"Sleep rejected by drivers\n");-return-EBUSY;-}+/* Notify old-style device drivers */+broadcast_sleep(PBOOK_SLEEP_REQUEST);/* Sync the disks. *//* XXX It would be nice to have some way to ensure that
@@ -2029,12 +2010,7 @@ pmac_suspend_devices(void)*/sys_sync();-/* Sleep can fail now. May not be very robust but useful for debugging */-ret=broadcast_sleep(PBOOK_SLEEP_NOW,PBOOK_WAKE);-if(ret!=PBOOK_SLEEP_OK){-printk(KERN_ERR"Driver sleep failed\n");-return-EBUSY;-}+broadcast_sleep(PBOOK_SLEEP_NOW);/* Send suspend call to devices, hold the device core's dpm_sem */ret=device_suspend(PMSG_SUSPEND);---linux-2.6-git.orig/include/linux/pmu.h2007-02-0722:33:32.817922690+0100+++linux-2.6-git/include/linux/pmu.h2007-02-0722:34:39.685922690+0100
@@ -168,24 +168,16 @@ extern int pmu_get_model(void);structpmu_sleep_notifier{-int(*notifier_call)(structpmu_sleep_notifier*self,intwhen);+void(*notifier_call)(structpmu_sleep_notifier*self,intwhen);intpriority;structlist_headlist;};/* Code values for calling sleep/wakeup handlers-*-*Note:Ifasleeprequestgotcancelled,alldriverswillget-*thePBOOK_SLEEP_REJECT,eventhosewhodidn'tgetthePBOOK_SLEEP_REQUEST.*/#define PBOOK_SLEEP_REQUEST 1#define PBOOK_SLEEP_NOW 2-#define PBOOK_SLEEP_REJECT 3-#define PBOOK_WAKE 4--/* Result codes returned by the notifiers */-#define PBOOK_SLEEP_OK 0-#define PBOOK_SLEEP_REFUSE -1+#define PBOOK_WAKE 3/* priority levels in notifiers */#define SLEEP_LEVEL_VIDEO 100 /* Video driver (first wake) */---linux-2.6-git.orig/drivers/macintosh/via-pmu-led.c2007-02-0722:33:32.737922690+0100+++linux-2.6-git/drivers/macintosh/via-pmu-led.c2007-02-0722:34:39.687922690+0100
@@ -1419,7 +1419,7 @@ load_awacs(void)*Savestatewhengoingtosleep,restoreitafterwards.*//* FIXME: sort out disabling/re-enabling of read stuff as well */-staticintawacs_sleep_notify(structpmu_sleep_notifier*self,intwhen)+staticvoidawacs_sleep_notify(structpmu_sleep_notifier*self,intwhen){unsignedlongflags;
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:16:51
This patch adds a dummy affinity callback for the IPI "chip" to avoid
getting a warning ("Cannot set affinity for irq 251") when taking a
CPU offline via sysfs or during suspend.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
@@ -700,6 +700,12 @@ static void mpic_end_ipi(unsigned int irmpic_eoi(mpic);}+staticvoidmpic_set_ipi_affinity(unsignedintirq,cpumask_tcpumask)+{+/* we can't set affinity but also don't need to since we only+*sendIPIstoonlineCPUs*/+}+#endif /* CONFIG_SMP */staticvoidmpic_set_affinity(unsignedintirq,cpumask_tcpumask)
@@ -83,7 +83,6 @@ int page_is_ram(unsigned long pfn)return0;#endif}-EXPORT_SYMBOL(page_is_ram);pgprot_tphys_mem_access_prot(structfile*file,unsignedlongpfn,unsignedlongsize,pgprot_tvma_prot)
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 13:28:41
This patch allows "hotplugging" of CPUs on G5 machines. CPUs that are
disabled are put into an idle loop with the decrementer frequency set
to minimum. To wake them up again we kick them just like when bringing
them up. To stop those CPUs from messing with any global state we stop
them from entering the timer interrupt.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
@@ -898,7 +898,7 @@ void smp_core99_cpu_die(unsigned int cpucpu_dead[cpu]=0;}-#endif+#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 *//* Core99 Macs (dual G4s and G5s) */structsmp_ops_tcore99_smp_ops={
@@ -908,8 +908,16 @@ struct smp_ops_t core99_smp_ops = {.setup_cpu=smp_core99_setup_cpu,.give_timebase=smp_core99_give_timebase,.take_timebase=smp_core99_take_timebase,-#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32)+#if defined(CONFIG_HOTPLUG_CPU)+# if defined(CONFIG_PPC32).cpu_disable=smp_core99_cpu_disable,.cpu_die=smp_core99_cpu_die,+# endif+# if defined(CONFIG_PPC64)+.cpu_disable=generic_cpu_disable,+.cpu_die=generic_cpu_die,+/* intentionally do *NOT* assign cpu_enable,+*thegenericcodewillusekick_cputhen!*/+# endif#endif};---linux-2.6-git.orig/arch/powerpc/platforms/powermac/setup.c2007-02-0812:52:47.722172211+0100+++linux-2.6-git/arch/powerpc/platforms/powermac/setup.c2007-02-0813:17:06.436651035+0100
@@ -490,6 +490,9 @@ static int pmac_late_init(void)#ifdef CONFIG_SOFTWARE_SUSPENDpm_set_ops(&pmac_pm_ops);#endif /* CONFIG_SOFTWARE_SUSPEND */+/* this is udbg (which is __init) and we can later use it during+*cpuhotplug(insmp_core99_kick_cpu)*/+ppc_md.progress=NULL;return0;}
@@ -716,6 +719,44 @@ static int pmac_pci_probe_mode(struct pcreturnPCI_PROBE_NORMAL;returnPCI_PROBE_DEVTREE;}++#ifdef CONFIG_HOTPLUG_CPU+/* access per cpu vars from generic smp.c */+DECLARE_PER_CPU(int,cpu_state);++staticvoidpmac_cpu_die(void)+{+/* turn off as much as possible, we'll be+*kickedoutasthiswillonlybeinvoked+*oncore99platformsfornow...*/++hard_irq_disable();++printk(KERN_INFO"CPU#%d offline\n",smp_processor_id());+__get_cpu_var(cpu_state)=CPU_DEAD;+smp_wmb();++/* during the path that leads here preemption is disabled,+*reenableitnowsothatwhencominguppreemptcountis+*zerocorrectly*/+preempt_enable();++while(1){+ppc64_runlatch_off();++/* let's not take timer interrupts too often ... */+set_dec(0x7fffffff);++if(ppc_md.power_save){+ppc_md.power_save();+}else{+HMT_low();+HMT_very_low();+}+}+}+#endif+#endifstaticvoid__initpmac_init_irq(void)
@@ -597,6 +597,11 @@ static void iSeries_tb_recal(void)}#endif+#ifdef CONFIG_CPU_HOTPLUG+/* from smp.c, see below in timer_interrupt() */+DECLARE_PER_CPU(int,cpu_state);+#endif+/**ForiSeriessharedprocessors,wehavetoletthehypervisor*setthehardwaredecrementer.Wesetavirtualdecrementer
@@ -619,6 +624,15 @@ void timer_interrupt(struct pt_regs * reunsignedlongticks;u64tb_next_jiffy;+#ifdef CONFIG_CPU_HOTPLUG+/* if we have fake CPU hotplug just to support suspend to disk+*andcan'treallyturnoffaCPU,itmaybetakingtimerinterrupts+*evenwhenitisdead.Avoiddoinganythinginthatcasesoglobal+*stateisnotmodifiedforandbyaCPUthatdoesn'treallyexist.*/+if(__get_cpu_var(cpu_state)==CPU_DEAD)+return;+#endif+#ifdef CONFIG_PPC32if(atomic_read(&ppc_n_lost_interrupts)!=0)do_IRQ(regs);
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-08 15:38:19
On Thu, 2007-02-08 at 14:28 +0100, Johannes Berg wrote:
+#ifdef CONFIG_CPU_HOTPLUG
+ /* if we have fake CPU hotplug just to support suspend to disk
+ * and can't really turn off a CPU, it may be taking timer interrupts
+ * even when it is dead. Avoid doing anything in that case so global
+ * state is not modified for and by a CPU that doesn't really exist. */
+ if (__get_cpu_var(cpu_state) == CPU_DEAD)
+ return;
+#endif
Michael Buesch suggested to add an unlikely() there which I'll do for
the next round.
johannes
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-02-08 21:14:12
On Thu, 2007-02-08 at 14:16 +0100, Johannes Berg wrote:
This patch adds a dummy affinity callback for the IPI "chip" to avoid
getting a warning ("Cannot set affinity for irq 251") when taking a
CPU offline via sysfs or during suspend.
Wouldn't be a better option to make sure they have IRQF_PERCPU set and
that we don't do the affinity thing on interrupts with that flag when
offlining a CPU ?
Ben.
quoted hunk
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
@@ -700,6 +700,12 @@ static void mpic_end_ipi(unsigned int irmpic_eoi(mpic);}+staticvoidmpic_set_ipi_affinity(unsignedintirq,cpumask_tcpumask)+{+/* we can't set affinity but also don't need to since we only+*sendIPIstoonlineCPUs*/+}+#endif /* CONFIG_SMP */staticvoidmpic_set_affinity(unsignedintirq,cpumask_tcpumask)
From: Paul Mackerras <hidden> Date: 2007-02-08 21:59:53
Johannes Berg writes:
+#ifdef CONFIG_CPU_HOTPLUG
+ /* if we have fake CPU hotplug just to support suspend to disk
+ * and can't really turn off a CPU, it may be taking timer interrupts
+ * even when it is dead. Avoid doing anything in that case so global
+ * state is not modified for and by a CPU that doesn't really exist. */
+ if (__get_cpu_var(cpu_state) == CPU_DEAD)
+ return;
+#endif
Are you sure this is necessary? IIRC, the if (!cpu_is_offline(cpu)) a
little bit further down is there to cope with this exact situation.
And cpu hotplug already works on G4 SMP powermacs, where we can't
physically remove the cpu either - we just put it into sleep mode with
interrupts disabled, which is what we would be doing on a G5 powermac
also.
Paul.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-02-08 22:24:03
On Thu, 2007-02-08 at 14:14 +0100, Johannes Berg wrote:
On Wed, 2007-02-07 at 13:45 +0100, Johannes Berg wrote:
quoted
Except for the in-irq count hack I'm happy with this. I still haven't found
where the in-hard-irq count is set to 1 in the down path during suspend or
resume and other platforms do similar things so I'm inclined to leave this.
Um, ok, so the hack breaks platforms that don't have paca, e.g. chrp32.
Also, I finally figured out how the in-hard-irq count happens. The thing
is that when I try to turn off the CPU it actually doesn't really turn
off of course, so it ends up doing NAP and taking timer interrupts...
which goes irq_enter() and we happen to kill it afterwards.
I have two ways of fixing this:
- just ignore it as we do now
- insert a "if (cpu_dead) return" into the timer interrupt function
I prefer the latter because then we're guaranteed that whatever the
timer interrupt does we don't modify any state for/by the CPU that isn't
supposed to exist.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-02-08 22:24:23
On Thu, 2007-02-08 at 14:14 +0100, Johannes Berg wrote:
On Wed, 2007-02-07 at 13:45 +0100, Johannes Berg wrote:
quoted
Except for the in-irq count hack I'm happy with this. I still haven't found
where the in-hard-irq count is set to 1 in the down path during suspend or
resume and other platforms do similar things so I'm inclined to leave this.
Um, ok, so the hack breaks platforms that don't have paca, e.g. chrp32.
Also, I finally figured out how the in-hard-irq count happens. The thing
is that when I try to turn off the CPU it actually doesn't really turn
off of course, so it ends up doing NAP and taking timer interrupts...
which goes irq_enter() and we happen to kill it afterwards.
I have two ways of fixing this:
- just ignore it as we do now
- insert a "if (cpu_dead) return" into the timer interrupt function
I prefer the latter because then we're guaranteed that whatever the
timer interrupt does we don't modify any state for/by the CPU that isn't
supposed to exist.
Or we can just mark interrupts as soft-disabled ?
Ben.
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-02-08 22:33:32
Are you sure this is necessary? IIRC, the if (!cpu_is_offline(cpu)) a
little bit further down is there to cope with this exact situation.
And cpu hotplug already works on G4 SMP powermacs, where we can't
physically remove the cpu either - we just put it into sleep mode with
interrupts disabled, which is what we would be doing on a G5 powermac
also.
I've always had doubts about wether SLEEP with EE off was legal and
wouldn't actually still go out of sleep when getting an interrupt... In
that case, at least on CPUs where the DEC acts "level" triggered, we
need to at least reset the DEC to some sane value.
Ben.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-09 15:39:21
On Fri, 2007-02-09 at 08:59 +1100, Paul Mackerras wrote:
Johannes Berg writes:
quoted
+#ifdef CONFIG_CPU_HOTPLUG
+ /* if we have fake CPU hotplug just to support suspend to disk
+ * and can't really turn off a CPU, it may be taking timer interrupts
+ * even when it is dead. Avoid doing anything in that case so global
+ * state is not modified for and by a CPU that doesn't really exist. */
+ if (__get_cpu_var(cpu_state) == CPU_DEAD)
+ return;
+#endif
Are you sure this is necessary? IIRC, the if (!cpu_is_offline(cpu)) a
little bit further down is there to cope with this exact situation.
Yes, but we still do irq_enter() and as it happens with HZ=1000 we
almost always kick the CPU out while it's inbetween irq_enter() and
irq_exit().
You said:
And cpu hotplug already works on G4 SMP powermacs, where we can't
physically remove the cpu either - we just put it into sleep mode with
interrupts disabled, which is what we would be doing on a G5 powermac
also.
and then Benh said:
I've always had doubts about wether SLEEP with EE off was legal and
wouldn't actually still go out of sleep when getting an interrupt... In
that case, at least on CPUs where the DEC acts "level" triggered, we
need to at least reset the DEC to some sane value.
I probably should've mentioned that. I tried going into NAP with EE off
and that seemed to get me a busy loop where the CPU wasn't even napping
at all. I could have done something wrong, of course.
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-09 15:41:08
On Fri, 2007-02-09 at 08:14 +1100, Benjamin Herrenschmidt wrote:
quoted
This patch adds a dummy affinity callback for the IPI "chip" to avoid
getting a warning ("Cannot set affinity for irq 251") when taking a
CPU offline via sysfs or during suspend.
Wouldn't be a better option to make sure they have IRQF_PERCPU set and
that we don't do the affinity thing on interrupts with that flag when
offlining a CPU ?
Hmm. Yeah, something like that could be possible. Looking at
fixup_irqs() there is a test:
if (irq_desc[irq].status & IRQ_PER_CPU)
continue;
but I'd have to dig what else this means.
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-12 15:15:54
This patch allows "hotplugging" of CPUs on G5 machines. CPUs that are
disabled are put into an idle loop with the decrementer frequency set
to minimum. To wake them up again we kick them just like when bringing
them up.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
This one seems better. I just soft-disable interrupts from the idle
function and exit it to reset the decrementer.
arch/powerpc/kernel/idle_power4.S | 14 +++++++++----
arch/powerpc/platforms/powermac/setup.c | 34 +++++++++++++++++++++++++++++++-
arch/powerpc/platforms/powermac/smp.c | 12 +++++++++--
include/asm-powerpc/machdep.h | 2 +
4 files changed, 55 insertions(+), 7 deletions(-)
@@ -897,7 +897,7 @@ void smp_core99_cpu_die(unsigned int cpucpu_dead[cpu]=0;}-#endif+#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 *//* Core99 Macs (dual G4s and G5s) */structsmp_ops_tcore99_smp_ops={
@@ -907,8 +907,16 @@ struct smp_ops_t core99_smp_ops = {.setup_cpu=smp_core99_setup_cpu,.give_timebase=smp_core99_give_timebase,.take_timebase=smp_core99_take_timebase,-#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32)+#if defined(CONFIG_HOTPLUG_CPU)+# if defined(CONFIG_PPC32).cpu_disable=smp_core99_cpu_disable,.cpu_die=smp_core99_cpu_die,+# endif+# if defined(CONFIG_PPC64)+.cpu_disable=generic_cpu_disable,+.cpu_die=generic_cpu_die,+/* intentionally do *NOT* assign cpu_enable,+*thegenericcodewillusekick_cputhen!*/+# endif#endif};---linux-2.6-git.orig/arch/powerpc/platforms/powermac/setup.c2007-02-1215:35:52.418522210+0100+++linux-2.6-git/arch/powerpc/platforms/powermac/setup.c2007-02-1215:39:42.512522210+0100
@@ -425,6 +425,9 @@ static int initializing = 1;staticintpmac_late_init(void){initializing=0;+/* this is udbg (which is __init) and we can later use it during+*cpuhotplug(insmp_core99_kick_cpu)*/+ppc_md.progress=NULL;return0;}
@@ -651,6 +654,35 @@ static int pmac_pci_probe_mode(struct pcreturnPCI_PROBE_NORMAL;returnPCI_PROBE_DEVTREE;}++#ifdef CONFIG_HOTPLUG_CPU+/* access per cpu vars from generic smp.c */+DECLARE_PER_CPU(int,cpu_state);++staticvoidpmac_cpu_die(void)+{+/* turn off as much as possible, we'll be+*kickedoutasthiswillonlybeinvoked+*oncore99platformsfornow...*/++printk(KERN_INFO"CPU#%d offline\n",smp_processor_id());+__get_cpu_var(cpu_state)=CPU_DEAD;+smp_wmb();++/* during the path that leads here preemption is disabled,+*reenableitnowsothatwhencominguppreemptcountis+*zerocorrectly*/+preempt_enable();++while(1){+/* let's not take timer interrupts too often ... */+set_dec(0x7fffffff);++power4_idle_irqs_softdisabled();+}+}+#endif+#endifdefine_machine(powermac){
@@ -51,5 +56,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)isyncmtmsrdr7isync-b1b-+beq-1b/* cr0 is still set, exit if this+ispower4_idle_irqs_softdisabled*/+blr---linux-2.6-git.orig/include/asm-powerpc/machdep.h2007-02-1215:35:52.551522210+0100+++linux-2.6-git/include/asm-powerpc/machdep.h2007-02-1215:36:13.006522210+0100
@@ -252,6 +252,8 @@ struct machdep_calls {externvoidpower4_idle(void);externvoidppc6xx_idle(void);+/* this one is for CPU hotplug (e.g. powermac) */+externvoidpower4_idle_irqs_softdisabled(void);/**ppc_mdcontainsacopyofthemachinedescriptionstructureforthe
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-12 15:20:18
This patch changes the MPIC IPIs to be per-CPU to avoid getting a
warning ("Cannot set affinity for irq 251") when taking a CPU
offline via sysfs or during suspend.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
Yup, that's a lot better, thanks.
arch/powerpc/sysdev/mpic.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Pavel Machek <hidden> Date: 2007-02-13 12:12:49
Hi!
This patch changes /sys/power/state to not advertise any valid states
(except for disk if SOFTWARE_SUSPEND is enabled) when no pm_ops have been
set so userspace can easily discover what states should be available.
Also, because the pm ops in powermac are obviously not using them as
intended, I added documentation for it in kernel-doc format.
Reordering the fields in struct pm_ops not only makes the output of
kernel-doc make more sense but also removes a hole from the structure on
64-bit platforms.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-pm@lists.osdl.org
Cc: Rafael J. Wysocki <redacted>
Cc: Pavel Macheck <redacted>
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-13 16:08:40
On Tue, 2007-02-13 at 13:12 +0100, Pavel Machek wrote:
Hi!
quoted
This patch changes /sys/power/state to not advertise any valid states
(except for disk if SOFTWARE_SUSPEND is enabled) when no pm_ops have been
set so userspace can easily discover what states should be available.
Also, because the pm ops in powermac are obviously not using them as
intended, I added documentation for it in kernel-doc format.
Reordering the fields in struct pm_ops not only makes the output of
kernel-doc make more sense but also removes a hole from the structure on
64-bit platforms.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-pm@lists.osdl.org
Cc: Rafael J. Wysocki <redacted>
Cc: Pavel Macheck <redacted>
Looks okay to me.
Are you going to pick it up or should I send it to someone else?
johannes
From: Pavel Machek <hidden> Date: 2007-02-13 16:14:23
Hi!
quoted
quoted
This patch changes /sys/power/state to not advertise any valid states
(except for disk if SOFTWARE_SUSPEND is enabled) when no pm_ops have been
set so userspace can easily discover what states should be available.
Also, because the pm ops in powermac are obviously not using them as
intended, I added documentation for it in kernel-doc format.
Reordering the fields in struct pm_ops not only makes the output of
kernel-doc make more sense but also removes a hole from the structure on
64-bit platforms.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-pm@lists.osdl.org
Cc: Rafael J. Wysocki <redacted>
Cc: Pavel Macheck <redacted>
Looks okay to me.
Are you going to pick it up or should I send it to someone else?
From: Pavel Machek <hidden> Date: 2007-02-14 14:45:56
Hi!
quoted
Except for the in-irq count hack I'm happy with this. I still haven't found
where the in-hard-irq count is set to 1 in the down path during suspend or
resume and other platforms do similar things so I'm inclined to leave this.
Um, ok, so the hack breaks platforms that don't have paca, e.g. chrp32.
Also, I finally figured out how the in-hard-irq count happens. The thing
is that when I try to turn off the CPU it actually doesn't really turn
off of course, so it ends up doing NAP and taking timer interrupts...
which goes irq_enter() and we happen to kill it afterwards.
I have two ways of fixing this:
- just ignore it as we do now
- insert a "if (cpu_dead) return" into the timer interrupt function
I prefer the latter because then we're guaranteed that whatever the
timer interrupt does we don't modify any state for/by the CPU that isn't
supposed to exist.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-14 19:11:36
On Wed, 2007-02-14 at 15:45 +0100, Pavel Machek wrote:
Can you disable timer interrupt on the interrupt controller, instead?
No, it's not actually an external interrupt, the "timer interrupt" on
powerpc is the decrementer exception which is built into the CPU.
In any case, the soft-irq disable stuff I already posted a bit later
provides the same with no more runtime overhead than we already have.
johannes
From: Benjamin Herrenschmidt <benh@kernel.crashing.org> Date: 2007-02-14 21:27:09
Can you disable timer interrupt on the interrupt controller, instead?
Provides same functionality, and needs no runtime overhead...
On most PowerPCs, timer interrupt aren't an interrupt comimg from the
interrupt controller, but their own processor exception triggered by the
decrementer defined by the ppc architecture.
Ben.
From: Paul Mackerras <hidden> Date: 2007-02-15 03:44:06
Johannes Berg writes:
quoted hunk
+_GLOBAL(power4_idle_irqs_softdisabled)
+ li r6,1
+ b 0f
_GLOBAL(power4_idle)
+ li r6,0
+0:
BEGIN_FTR_SECTION
blr
END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP)
Don't you have something backwards here? If we come in through
power4_idle_irqs_softdisabled, the name would indicate to me that we
want to end up with paca->soft_enabled set to 0, not 1.
Paul.
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-15 15:08:28
On Thu, 2007-02-15 at 14:44 +1100, Paul Mackerras wrote:
quoted
+_GLOBAL(power4_idle_irqs_softdisabled)
+ li r6,1
+ b 0f
_GLOBAL(power4_idle)
+ li r6,0
+0:
BEGIN_FTR_SECTION
blr
END_FTR_SECTION_IFCLR(CPU_FTR_CAN_NAP)
Don't you have something backwards here? If we come in through
power4_idle_irqs_softdisabled, the name would indicate to me that we
want to end up with paca->soft_enabled set to 0, not 1.
Umm, yes, looks like. I'll rewrite it. Interesting that it worked as I
had expected though (I actually put a printk into the timer interrupt!).
johannes
From: Johannes Berg <johannes@sipsolutions.net> Date: 2007-02-15 19:43:49
This patch allows "hotplugging" of CPUs on G5 machines. CPUs that are
disabled are put into an idle loop with the decrementer frequency set
to minimum. To wake them up again we kick them just like when bringing
them up. To stop those CPUs from messing with any global state we stop
them from entering the timer interrupt.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
This makes more sense... Thanks for pointing that out. I wonder why it
actually worked.
arch/powerpc/kernel/idle_power4.S | 15 ++++++++++----
arch/powerpc/platforms/powermac/setup.c | 34 +++++++++++++++++++++++++++++++-
arch/powerpc/platforms/powermac/smp.c | 12 +++++++++--
include/asm-powerpc/machdep.h | 2 +
4 files changed, 56 insertions(+), 7 deletions(-)
@@ -897,7 +897,7 @@ void smp_core99_cpu_die(unsigned int cpucpu_dead[cpu]=0;}-#endif+#endif /* CONFIG_HOTPLUG_CPU && CONFIG_PP32 *//* Core99 Macs (dual G4s and G5s) */structsmp_ops_tcore99_smp_ops={
@@ -907,8 +907,16 @@ struct smp_ops_t core99_smp_ops = {.setup_cpu=smp_core99_setup_cpu,.give_timebase=smp_core99_give_timebase,.take_timebase=smp_core99_take_timebase,-#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_PPC32)+#if defined(CONFIG_HOTPLUG_CPU)+# if defined(CONFIG_PPC32).cpu_disable=smp_core99_cpu_disable,.cpu_die=smp_core99_cpu_die,+# endif+# if defined(CONFIG_PPC64)+.cpu_disable=generic_cpu_disable,+.cpu_die=generic_cpu_die,+/* intentionally do *NOT* assign cpu_enable,+*thegenericcodewillusekick_cputhen!*/+# endif#endif};---linux-2.6-git.orig/arch/powerpc/platforms/powermac/setup.c2007-02-1219:14:32.182037567+0100+++linux-2.6-git/arch/powerpc/platforms/powermac/setup.c2007-02-1219:14:32.347037567+0100
@@ -425,6 +425,9 @@ static int initializing = 1;staticintpmac_late_init(void){initializing=0;+/* this is udbg (which is __init) and we can later use it during+*cpuhotplug(insmp_core99_kick_cpu)*/+ppc_md.progress=NULL;return0;}
@@ -651,6 +654,35 @@ static int pmac_pci_probe_mode(struct pcreturnPCI_PROBE_NORMAL;returnPCI_PROBE_DEVTREE;}++#ifdef CONFIG_HOTPLUG_CPU+/* access per cpu vars from generic smp.c */+DECLARE_PER_CPU(int,cpu_state);++staticvoidpmac_cpu_die(void)+{+/* turn off as much as possible, we'll be+*kickedoutasthiswillonlybeinvoked+*oncore99platformsfornow...*/++printk(KERN_INFO"CPU#%d offline\n",smp_processor_id());+__get_cpu_var(cpu_state)=CPU_DEAD;+smp_wmb();++/* during the path that leads here preemption is disabled,+*reenableitnowsothatwhencominguppreemptcountis+*zerocorrectly*/+preempt_enable();++while(1){+/* let's not take timer interrupts too often ... */+set_dec(0x7fffffff);++power4_idle_irqs_softdisabled();+}+}+#endif+#endifdefine_machine(powermac){
@@ -252,6 +252,8 @@ struct machdep_calls {externvoidpower4_idle(void);externvoidppc6xx_idle(void);+/* this one is for CPU hotplug (e.g. powermac) */+externvoidpower4_idle_irqs_softdisabled(void);/**ppc_mdcontainsacopyofthemachinedescriptionstructureforthe