diff options
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/83xx/suspend.c | 52 | ||||
-rw-r--r-- | arch/powerpc/platforms/85xx/mpc85xx_mds.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/flipper-pic.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/hlwd-pic.c | 10 | ||||
-rw-r--r-- | arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/mf.c | 147 | ||||
-rw-r--r-- | arch/powerpc/platforms/iseries/viopath.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/Kconfig | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/cmm.c | 254 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/dlpar.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/smp.c | 4 |
11 files changed, 398 insertions, 85 deletions
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c index d306f07b9aa..43805348b81 100644 --- a/arch/powerpc/platforms/83xx/suspend.c +++ b/arch/powerpc/platforms/83xx/suspend.c @@ -32,6 +32,7 @@ #define PMCCR1_NEXT_STATE 0x0C /* Next state for power management */ #define PMCCR1_NEXT_STATE_SHIFT 2 #define PMCCR1_CURR_STATE 0x03 /* Current state for power management*/ +#define IMMR_SYSCR_OFFSET 0x100 #define IMMR_RCW_OFFSET 0x900 #define RCW_PCI_HOST 0x80000000 @@ -78,6 +79,22 @@ struct mpc83xx_clock { u32 sccr; }; +struct mpc83xx_syscr { + __be32 sgprl; + __be32 sgprh; + __be32 spridr; + __be32 :32; + __be32 spcr; + __be32 sicrl; + __be32 sicrh; +}; + +struct mpc83xx_saved { + u32 sicrl; + u32 sicrh; + u32 sccr; +}; + struct pmc_type { int has_deep_sleep; }; @@ -87,6 +104,8 @@ static int has_deep_sleep, deep_sleeping; static int pmc_irq; static struct mpc83xx_pmc __iomem *pmc_regs; static struct mpc83xx_clock __iomem *clock_regs; +static struct mpc83xx_syscr __iomem *syscr_regs; +static struct mpc83xx_saved saved_regs; static int is_pci_agent, wake_from_pci; static phys_addr_t immrbase; static int pci_pm_state; @@ -137,6 +156,20 @@ static irqreturn_t pmc_irq_handler(int irq, void *dev_id) return ret; } +static void mpc83xx_suspend_restore_regs(void) +{ + out_be32(&syscr_regs->sicrl, saved_regs.sicrl); + out_be32(&syscr_regs->sicrh, saved_regs.sicrh); + out_be32(&clock_regs->sccr, saved_regs.sccr); +} + +static void mpc83xx_suspend_save_regs(void) +{ + saved_regs.sicrl = in_be32(&syscr_regs->sicrl); + saved_regs.sicrh = in_be32(&syscr_regs->sicrh); + saved_regs.sccr = in_be32(&clock_regs->sccr); +} + static int mpc83xx_suspend_enter(suspend_state_t state) { int ret = -EAGAIN; @@ -166,6 +199,8 @@ static int mpc83xx_suspend_enter(suspend_state_t state) */ if (deep_sleeping) { + mpc83xx_suspend_save_regs(); + out_be32(&pmc_regs->mask, PMCER_ALL); out_be32(&pmc_regs->config1, @@ -179,6 +214,8 @@ static int mpc83xx_suspend_enter(suspend_state_t state) in_be32(&pmc_regs->config1) & ~PMCCR1_POWER_OFF); out_be32(&pmc_regs->mask, PMCER_PMCI); + + mpc83xx_suspend_restore_regs(); } else { out_be32(&pmc_regs->mask, PMCER_PMCI); @@ -194,7 +231,7 @@ out: return ret; } -static void mpc83xx_suspend_finish(void) +static void mpc83xx_suspend_end(void) { deep_sleeping = 0; } @@ -278,7 +315,7 @@ static struct platform_suspend_ops mpc83xx_suspend_ops = { .valid = mpc83xx_suspend_valid, .begin = mpc83xx_suspend_begin, .enter = mpc83xx_suspend_enter, - .finish = mpc83xx_suspend_finish, + .end = mpc83xx_suspend_end, }; static int pmc_probe(struct of_device *ofdev, @@ -333,12 +370,23 @@ static int pmc_probe(struct of_device *ofdev, goto out_pmc; } + if (has_deep_sleep) { + syscr_regs = ioremap(immrbase + IMMR_SYSCR_OFFSET, + sizeof(*syscr_regs)); + if (!syscr_regs) { + ret = -ENOMEM; + goto out_syscr; + } + } + if (is_pci_agent) mpc83xx_set_agent(); suspend_set_ops(&mpc83xx_suspend_ops); return 0; +out_syscr: + iounmap(clock_regs); out_pmc: iounmap(pmc_regs); out: diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c index c5028a2e5a5..21f61b8c445 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c @@ -86,7 +86,7 @@ static int mpc8568_fixup_125_clock(struct phy_device *phydev) scr = phy_read(phydev, MV88E1111_SCR); if (scr < 0) - return err; + return scr; err = phy_write(phydev, MV88E1111_SCR, scr | 0x0008); diff --git a/arch/powerpc/platforms/embedded6xx/flipper-pic.c b/arch/powerpc/platforms/embedded6xx/flipper-pic.c index d5963285e3b..c278bd3a8fe 100644 --- a/arch/powerpc/platforms/embedded6xx/flipper-pic.c +++ b/arch/powerpc/platforms/embedded6xx/flipper-pic.c @@ -102,7 +102,7 @@ static int flipper_pic_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hwirq) { set_irq_chip_data(virq, h->host_data); - get_irq_desc(virq)->status |= IRQ_LEVEL; + irq_to_desc(virq)->status |= IRQ_LEVEL; set_irq_chip_and_handler(virq, &flipper_pic, handle_level_irq); return 0; } diff --git a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c index dd20bff3320..a771f91e215 100644 --- a/arch/powerpc/platforms/embedded6xx/hlwd-pic.c +++ b/arch/powerpc/platforms/embedded6xx/hlwd-pic.c @@ -95,7 +95,7 @@ static int hlwd_pic_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hwirq) { set_irq_chip_data(virq, h->host_data); - get_irq_desc(virq)->status |= IRQ_LEVEL; + irq_to_desc(virq)->status |= IRQ_LEVEL; set_irq_chip_and_handler(virq, &hlwd_pic, handle_level_irq); return 0; } @@ -132,9 +132,9 @@ static void hlwd_pic_irq_cascade(unsigned int cascade_virq, struct irq_host *irq_host = get_irq_data(cascade_virq); unsigned int virq; - spin_lock(&desc->lock); + raw_spin_lock(&desc->lock); desc->chip->mask(cascade_virq); /* IRQ_LEVEL */ - spin_unlock(&desc->lock); + raw_spin_unlock(&desc->lock); virq = __hlwd_pic_get_irq(irq_host); if (virq != NO_IRQ) @@ -142,11 +142,11 @@ static void hlwd_pic_irq_cascade(unsigned int cascade_virq, else pr_err("spurious interrupt!\n"); - spin_lock(&desc->lock); + raw_spin_lock(&desc->lock); desc->chip->ack(cascade_virq); /* IRQ_LEVEL */ if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask) desc->chip->unmask(cascade_virq); - spin_unlock(&desc->lock); + raw_spin_unlock(&desc->lock); } /* diff --git a/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c index edc956cc8b1..20a8ed91962 100644 --- a/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c +++ b/arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c @@ -120,7 +120,7 @@ static void ug_putc(char ch) while (!ug_is_txfifo_ready() && count--) barrier(); - if (count) + if (count >= 0) ug_raw_putc(ch); } diff --git a/arch/powerpc/platforms/iseries/mf.c b/arch/powerpc/platforms/iseries/mf.c index 0d9343df35b..6617915bcb1 100644 --- a/arch/powerpc/platforms/iseries/mf.c +++ b/arch/powerpc/platforms/iseries/mf.c @@ -855,59 +855,58 @@ static int mf_get_boot_rtc(struct rtc_time *tm) } #ifdef CONFIG_PROC_FS - -static int proc_mf_dump_cmdline(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int mf_cmdline_proc_show(struct seq_file *m, void *v) { - int len; - char *p; + char *page, *p; struct vsp_cmd_data vsp_cmd; int rc; dma_addr_t dma_addr; /* The HV appears to return no more than 256 bytes of command line */ - if (off >= 256) - return 0; - if ((off + count) > 256) - count = 256 - off; + page = kmalloc(256, GFP_KERNEL); + if (!page) + return -ENOMEM; - dma_addr = iseries_hv_map(page, off + count, DMA_FROM_DEVICE); - if (dma_addr == DMA_ERROR_CODE) + dma_addr = iseries_hv_map(page, 256, DMA_FROM_DEVICE); + if (dma_addr == DMA_ERROR_CODE) { + kfree(page); return -ENOMEM; - memset(page, 0, off + count); + } + memset(page, 0, 256); memset(&vsp_cmd, 0, sizeof(vsp_cmd)); vsp_cmd.cmd = 33; vsp_cmd.sub_data.kern.token = dma_addr; vsp_cmd.sub_data.kern.address_type = HvLpDma_AddressType_TceIndex; - vsp_cmd.sub_data.kern.side = (u64)data; - vsp_cmd.sub_data.kern.length = off + count; + vsp_cmd.sub_data.kern.side = (u64)m->private; + vsp_cmd.sub_data.kern.length = 256; mb(); rc = signal_vsp_instruction(&vsp_cmd); - iseries_hv_unmap(dma_addr, off + count, DMA_FROM_DEVICE); - if (rc) + iseries_hv_unmap(dma_addr, 256, DMA_FROM_DEVICE); + if (rc) { + kfree(page); return rc; - if (vsp_cmd.result_code != 0) + } + if (vsp_cmd.result_code != 0) { + kfree(page); return -ENOMEM; + } p = page; - len = 0; - while (len < (off + count)) { - if ((*p == '\0') || (*p == '\n')) { - if (*p == '\0') - *p = '\n'; - p++; - len++; - *eof = 1; + while (p - page < 256) { + if (*p == '\0' || *p == '\n') { + *p = '\n'; break; } p++; - len++; - } - if (len < off) { - *eof = 1; - len = 0; } - return len; + seq_write(m, page, p - page); + kfree(page); + return 0; +} + +static int mf_cmdline_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mf_cmdline_proc_show, PDE(inode)->data); } #if 0 @@ -962,10 +961,8 @@ static int proc_mf_dump_vmlinux(char *page, char **start, off_t off, } #endif -static int proc_mf_dump_side(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int mf_side_proc_show(struct seq_file *m, void *v) { - int len; char mf_current_side = ' '; struct vsp_cmd_data vsp_cmd; @@ -989,21 +986,17 @@ static int proc_mf_dump_side(char *page, char **start, off_t off, } } - len = sprintf(page, "%c\n", mf_current_side); + seq_printf(m, "%c\n", mf_current_side); + return 0; +} - if (len <= (off + count)) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - return len; +static int mf_side_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mf_side_proc_show, NULL); } -static int proc_mf_change_side(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t mf_side_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) { char side; u64 newSide; @@ -1041,6 +1034,15 @@ static int proc_mf_change_side(struct file *file, const char __user *buffer, return count; } +static const struct file_operations mf_side_proc_fops = { + .owner = THIS_MODULE, + .open = mf_side_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = mf_side_proc_write, +}; + #if 0 static void mf_getSrcHistory(char *buffer, int size) { @@ -1087,8 +1089,7 @@ static void mf_getSrcHistory(char *buffer, int size) } #endif -static int proc_mf_dump_src(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int mf_src_proc_show(struct seq_file *m, void *v) { #if 0 int len; @@ -1109,8 +1110,13 @@ static int proc_mf_dump_src(char *page, char **start, off_t off, #endif } -static int proc_mf_change_src(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static int mf_src_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mf_src_proc_show, NULL); +} + +static ssize_t mf_src_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) { char stkbuf[10]; @@ -1135,9 +1141,19 @@ static int proc_mf_change_src(struct file *file, const char __user *buffer, return count; } -static int proc_mf_change_cmdline(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static const struct file_operations mf_src_proc_fops = { + .owner = THIS_MODULE, + .open = mf_src_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = mf_src_proc_write, +}; + +static ssize_t mf_cmdline_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) { + void *data = PDE(file->f_path.dentry->d_inode)->data; struct vsp_cmd_data vsp_cmd; dma_addr_t dma_addr; char *page; @@ -1172,6 +1188,15 @@ out: return ret; } +static const struct file_operations mf_cmdline_proc_fops = { + .owner = THIS_MODULE, + .open = mf_cmdline_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = mf_cmdline_proc_write, +}; + static ssize_t proc_mf_change_vmlinux(struct file *file, const char __user *buf, size_t count, loff_t *ppos) @@ -1246,12 +1271,10 @@ static int __init mf_proc_init(void) if (!mf) return 1; - ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf); + ent = proc_create_data("cmdline", S_IRUSR|S_IWUSR, mf, + &mf_cmdline_proc_fops, (void *)(long)i); if (!ent) return 1; - ent->data = (void *)(long)i; - ent->read_proc = proc_mf_dump_cmdline; - ent->write_proc = proc_mf_change_cmdline; if (i == 3) /* no vmlinux entry for 'D' */ continue; @@ -1263,19 +1286,15 @@ static int __init mf_proc_init(void) return 1; } - ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + ent = proc_create("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root, + &mf_side_proc_fops); if (!ent) return 1; - ent->data = (void *)0; - ent->read_proc = proc_mf_dump_side; - ent->write_proc = proc_mf_change_side; - ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + ent = proc_create("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root, + &mf_src_proc_fops); if (!ent) return 1; - ent->data = (void *)0; - ent->read_proc = proc_mf_dump_src; - ent->write_proc = proc_mf_change_src; return 0; } diff --git a/arch/powerpc/platforms/iseries/viopath.c b/arch/powerpc/platforms/iseries/viopath.c index 49ff4dc422b..5aea94f3083 100644 --- a/arch/powerpc/platforms/iseries/viopath.c +++ b/arch/powerpc/platforms/iseries/viopath.c @@ -116,7 +116,7 @@ static int proc_viopath_show(struct seq_file *m, void *v) u16 vlanMap; dma_addr_t handle; HvLpEvent_Rc hvrc; - DECLARE_COMPLETION(done); + DECLARE_COMPLETION_ONSTACK(done); struct device_node *node; const char *sysid; diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig index 27554c807fd..c667f0f02c3 100644 --- a/arch/powerpc/platforms/pseries/Kconfig +++ b/arch/powerpc/platforms/pseries/Kconfig @@ -2,6 +2,8 @@ config PPC_PSERIES depends on PPC64 && PPC_BOOK3S bool "IBM pSeries & new (POWER5-based) iSeries" select MPIC + select PCI_MSI + select XICS select PPC_I8259 select PPC_RTAS select PPC_RTAS_DAEMON diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index bcdcf0ccc8d..a277f2e28db 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -38,19 +38,28 @@ #include <asm/mmu.h> #include <asm/pgalloc.h> #include <asm/uaccess.h> +#include <linux/memory.h> #include "plpar_wrappers.h" #define CMM_DRIVER_VERSION "1.0.0" #define CMM_DEFAULT_DELAY 1 +#define CMM_HOTPLUG_DELAY 5 #define CMM_DEBUG 0 #define CMM_DISABLE 0 #define CMM_OOM_KB 1024 #define CMM_MIN_MEM_MB 256 #define KB2PAGES(_p) ((_p)>>(PAGE_SHIFT-10)) #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) +/* + * The priority level tries to ensure that this notifier is called as + * late as possible to reduce thrashing in the shared memory pool. + */ +#define CMM_MEM_HOTPLUG_PRI 1 +#define CMM_MEM_ISOLATE_PRI 15 static unsigned int delay = CMM_DEFAULT_DELAY; +static unsigned int hotplug_delay = CMM_HOTPLUG_DELAY; static unsigned int oom_kb = CMM_OOM_KB; static unsigned int cmm_debug = CMM_DEBUG; static unsigned int cmm_disabled = CMM_DISABLE; @@ -65,6 +74,10 @@ MODULE_VERSION(CMM_DRIVER_VERSION); module_param_named(delay, delay, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(delay, "Delay (in seconds) between polls to query hypervisor paging requests. " "[Default=" __stringify(CMM_DEFAULT_DELAY) "]"); +module_param_named(hotplug_delay, hotplug_delay, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(delay, "Delay (in seconds) after memory hotplug remove " + "before loaning resumes. " + "[Default=" __stringify(CMM_HOTPLUG_DELAY) "]"); module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. " "[Default=" __stringify(CMM_OOM_KB) "]"); @@ -92,6 +105,9 @@ static unsigned long oom_freed_pages; static struct cmm_page_array *cmm_page_list; static DEFINE_SPINLOCK(cmm_lock); +static DEFINE_MUTEX(hotplug_mutex); +static int hotplug_occurred; /* protected by the hotplug mutex */ + static struct task_struct *cmm_thread_ptr; /** @@ -110,6 +126,17 @@ static long cmm_alloc_pages(long nr) cmm_dbg("Begin request for %ld pages\n", nr); while (nr) { + /* Exit if a hotplug operation is in progress or occurred */ + if (mutex_trylock(&hotplug_mutex)) { + if (hotplug_occurred) { + mutex_unlock(&hotplug_mutex); + break; + } + mutex_unlock(&hotplug_mutex); + } else { + break; + } + addr = __get_free_page(GFP_NOIO | __GFP_NOWARN | __GFP_NORETRY | __GFP_NOMEMALLOC); if (!addr) @@ -119,8 +146,9 @@ static long cmm_alloc_pages(long nr) if (!pa || pa->index >= CMM_NR_PAGES) { /* Need a new page for the page list. */ spin_unlock(&cmm_lock); - npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN | - __GFP_NORETRY | __GFP_NOMEMALLOC); + npa = (struct cmm_page_array *)__get_free_page( + GFP_NOIO | __GFP_NOWARN | + __GFP_NORETRY | __GFP_NOMEMALLOC); if (!npa) { pr_info("%s: Can not allocate new page list\n", __func__); free_page(addr); @@ -282,9 +310,28 @@ static int cmm_thread(void *dummy) while (1) { timeleft = msleep_interruptible(delay * 1000); - if (kthread_should_stop() || timeleft) { - loaned_pages_target = loaned_pages; + if (kthread_should_stop() || timeleft) break; + + if (mutex_trylock(&hotplug_mutex)) { + if (hotplug_occurred) { + hotplug_occurred = 0; + mutex_unlock(&hotplug_mutex); + cmm_dbg("Hotplug operation has occurred, " + "loaning activity suspended " + "for %d seconds.\n", + hotplug_delay); + timeleft = msleep_interruptible(hotplug_delay * + 1000); + if (kthread_should_stop() || timeleft) + break; + continue; + } + mutex_unlock(&hotplug_mutex); + } else { + cmm_dbg("Hotplug operation in progress, activity " + "suspended\n"); + continue; } cmm_get_mpp(); @@ -414,6 +461,193 @@ static struct notifier_block cmm_reboot_nb = { }; /** + * cmm_count_pages - Count the number of pages loaned in a particular range. + * + * @arg: memory_isolate_notify structure with address range and count + * + * Return value: + * 0 on success + **/ +static unsigned long cmm_count_pages(void *arg) +{ + struct memory_isolate_notify *marg = arg; + struct cmm_page_array *pa; + unsigned long start = (unsigned long)pfn_to_kaddr(marg->start_pfn); + unsigned long end = start + (marg->nr_pages << PAGE_SHIFT); + unsigned long idx; + + spin_lock(&cmm_lock); + pa = cmm_page_list; + while (pa) { + if ((unsigned long)pa >= start && (unsigned long)pa < end) + marg->pages_found++; + for (idx = 0; idx < pa->index; idx++) + if (pa->page[idx] >= start && pa->page[idx] < end) + marg->pages_found++; + pa = pa->next; + } + spin_unlock(&cmm_lock); + return 0; +} + +/** + * cmm_memory_isolate_cb - Handle memory isolation notifier calls + * @self: notifier block struct + * @action: action to take + * @arg: struct memory_isolate_notify data for handler + * + * Return value: + * NOTIFY_OK or notifier error based on subfunction return value + **/ +static int cmm_memory_isolate_cb(struct notifier_block *self, + unsigned long action, void *arg) +{ + int ret = 0; + + if (action == MEM_ISOLATE_COUNT) + ret = cmm_count_pages(arg); + + if (ret) + ret = notifier_from_errno(ret); + else + ret = NOTIFY_OK; + + return ret; +} + +static struct notifier_block cmm_mem_isolate_nb = { + .notifier_call = cmm_memory_isolate_cb, + .priority = CMM_MEM_ISOLATE_PRI +}; + +/** + * cmm_mem_going_offline - Unloan pages where memory is to be removed + * @arg: memory_notify structure with page range to be offlined + * + * Return value: + * 0 on success + **/ +static int cmm_mem_going_offline(void *arg) +{ + struct memory_notify *marg = arg; + unsigned long start_page = (unsigned long)pfn_to_kaddr(marg->start_pfn); + unsigned long end_page = start_page + (marg->nr_pages << PAGE_SHIFT); + struct cmm_page_array *pa_curr, *pa_last, *npa; + unsigned long idx; + unsigned long freed = 0; + + cmm_dbg("Memory going offline, searching 0x%lx (%ld pages).\n", + start_page, marg->nr_pages); + spin_lock(&cmm_lock); + + /* Search the page list for pages in the range to be offlined */ + pa_last = pa_curr = cmm_page_list; + while (pa_curr) { + for (idx = (pa_curr->index - 1); (idx + 1) > 0; idx--) { + if ((pa_curr->page[idx] < start_page) || + (pa_curr->page[idx] >= end_page)) + continue; + + plpar_page_set_active(__pa(pa_curr->page[idx])); + free_page(pa_curr->page[idx]); + freed++; + loaned_pages--; + totalram_pages++; + pa_curr->page[idx] = pa_last->page[--pa_last->index]; + if (pa_last->index == 0) { + if (pa_curr == pa_last) + pa_curr = pa_last->next; + pa_last = pa_last->next; + free_page((unsigned long)cmm_page_list); + cmm_page_list = pa_last; + continue; + } + } + pa_curr = pa_curr->next; + } + + /* Search for page list structures in the range to be offlined */ + pa_last = NULL; + pa_curr = cmm_page_list; + while (pa_curr) { + if (((unsigned long)pa_curr >= start_page) && + ((unsigned long)pa_curr < end_page)) { + npa = (struct cmm_page_array *)__get_free_page( + GFP_NOIO | __GFP_NOWARN | + __GFP_NORETRY | __GFP_NOMEMALLOC); + if (!npa) { + spin_unlock(&cmm_lock); + cmm_dbg("Failed to allocate memory for list " + "management. Memory hotplug " + "failed.\n"); + return ENOMEM; + } + memcpy(npa, pa_curr, PAGE_SIZE); + if (pa_curr == cmm_page_list) + cmm_page_list = npa; + if (pa_last) + pa_last->next = npa; + free_page((unsigned long) pa_curr); + freed++; + pa_curr = npa; + } + + pa_last = pa_curr; + pa_curr = pa_curr->next; + } + + spin_unlock(&cmm_lock); + cmm_dbg("Released %ld pages in the search range.\n", freed); + + return 0; +} + +/** + * cmm_memory_cb - Handle memory hotplug notifier calls + * @self: notifier block struct + * @action: action to take + * @arg: struct memory_notify data for handler + * + * Return value: + * NOTIFY_OK or notifier error based on subfunction return value + * + **/ +static int cmm_memory_cb(struct notifier_block *self, + unsigned long action, void *arg) +{ + int ret = 0; + + switch (action) { + case MEM_GOING_OFFLINE: + mutex_lock(&hotplug_mutex); + hotplug_occurred = 1; + ret = cmm_mem_going_offline(arg); + break; + case MEM_OFFLINE: + case MEM_CANCEL_OFFLINE: + mutex_unlock(&hotplug_mutex); + cmm_dbg("Memory offline operation complete.\n"); + break; + case MEM_GOING_ONLINE: + case MEM_ONLINE: + case MEM_CANCEL_ONLINE: + break; + } + + if (ret) + ret = notifier_from_errno(ret); + else + ret = NOTIFY_OK; + + return ret; +} + +static struct notifier_block cmm_mem_nb = { + .notifier_call = cmm_memory_cb, + .priority = CMM_MEM_HOTPLUG_PRI +}; + +/** * cmm_init - Module initialization * * Return value: @@ -435,18 +669,24 @@ static int cmm_init(void) if ((rc = cmm_sysfs_register(&cmm_sysdev))) goto out_reboot_notifier; + if (register_memory_notifier(&cmm_mem_nb) || + register_memory_isolate_notifier(&cmm_mem_isolate_nb)) + goto out_unregister_notifier; + if (cmm_disabled) return rc; cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); if (IS_ERR(cmm_thread_ptr)) { rc = PTR_ERR(cmm_thread_ptr); - goto out_unregister_sysfs; + goto out_unregister_notifier; } return rc; -out_unregister_sysfs: +out_unregister_notifier: + unregister_memory_notifier(&cmm_mem_nb); + unregister_memory_isolate_notifier(&cmm_mem_isolate_nb); cmm_unregister_sysfs(&cmm_sysdev); out_reboot_notifier: unregister_reboot_notifier(&cmm_reboot_nb); @@ -467,6 +707,8 @@ static void cmm_exit(void) kthread_stop(cmm_thread_ptr); unregister_oom_notifier(&cmm_oom_nb); unregister_reboot_notifier(&cmm_reboot_nb); + unregister_memory_notifier(&cmm_mem_nb); + unregister_memory_isolate_notifier(&cmm_mem_isolate_nb); cmm_free_pages(loaned_pages); cmm_unregister_sysfs(&cmm_sysdev); } diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index 12df9e8812a..67b7a10f9fc 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c @@ -346,12 +346,14 @@ int dlpar_release_drc(u32 drc_index) static DEFINE_MUTEX(pseries_cpu_hotplug_mutex); -void cpu_hotplug_driver_lock() +void cpu_hotplug_driver_lock(void) +__acquires(pseries_cpu_hotplug_mutex) { mutex_lock(&pseries_cpu_hotplug_mutex); } -void cpu_hotplug_driver_unlock() +void cpu_hotplug_driver_unlock(void) +__releases(pseries_cpu_hotplug_mutex) { mutex_unlock(&pseries_cpu_hotplug_mutex); } diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c index 8868c012268..b4886635972 100644 --- a/arch/powerpc/platforms/pseries/smp.c +++ b/arch/powerpc/platforms/pseries/smp.c @@ -144,8 +144,8 @@ static void __devinit smp_pSeries_kick_cpu(int nr) hcpuid = get_hard_smp_processor_id(nr); rc = plpar_hcall_norets(H_PROD, hcpuid); if (rc != H_SUCCESS) - panic("Error: Prod to wake up processor %d Ret= %ld\n", - nr, rc); + printk(KERN_ERR "Error: Prod to wake up processor %d\ + Ret= %ld\n", nr, rc); } } |