diff options
Diffstat (limited to 'arch/powerpc/kernel/rtasd.c')
| -rw-r--r-- | arch/powerpc/kernel/rtasd.c | 87 |
1 files changed, 75 insertions, 12 deletions
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c index 2e4832ab210..e736387fee6 100644 --- a/arch/powerpc/kernel/rtasd.c +++ b/arch/powerpc/kernel/rtasd.c @@ -20,14 +20,16 @@ #include <linux/spinlock.h> #include <linux/cpu.h> #include <linux/workqueue.h> +#include <linux/slab.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/rtas.h> #include <asm/prom.h> #include <asm/nvram.h> -#include <asm/atomic.h> +#include <linux/atomic.h> #include <asm/machdep.h> +#include <asm/topology.h> static DEFINE_SPINLOCK(rtasd_log_lock); @@ -86,6 +88,8 @@ static char *rtas_event_type(int type) return "Resource Deallocation Event"; case RTAS_TYPE_DUMP: return "Dump Notification Event"; + case RTAS_TYPE_PRRN: + return "Platform Resource Reassignment Event"; } return rtas_type[0]; @@ -146,8 +150,8 @@ static void printk_log_rtas(char *buf, int len) struct rtas_error_log *errlog = (struct rtas_error_log *)buf; printk(RTAS_DEBUG "event: %d, Type: %s, Severity: %d\n", - error_log_cnt, rtas_event_type(errlog->type), - errlog->severity); + error_log_cnt, rtas_event_type(rtas_error_type(errlog)), + rtas_error_severity(errlog)); } } @@ -155,14 +159,16 @@ static int log_rtas_len(char * buf) { int len; struct rtas_error_log *err; + uint32_t extended_log_length; /* rtas fixed header */ len = 8; err = (struct rtas_error_log *)buf; - if (err->extended_log_length) { + extended_log_length = rtas_error_extended_log_length(err); + if (rtas_error_extended(err) && extended_log_length) { /* extended header */ - len += err->extended_log_length; + len += extended_log_length; } if (rtas_error_log_max == 0) @@ -264,9 +270,49 @@ void pSeries_log_error(char *buf, unsigned int err_type, int fatal) spin_unlock_irqrestore(&rtasd_log_lock, s); return; } +} + +#ifdef CONFIG_PPC_PSERIES +static s32 prrn_update_scope; + +static void prrn_work_fn(struct work_struct *work) +{ + /* + * For PRRN, we must pass the negative of the scope value in + * the RTAS event. + */ + pseries_devicetree_update(-prrn_update_scope); +} +static DECLARE_WORK(prrn_work, prrn_work_fn); + +void prrn_schedule_update(u32 scope) +{ + flush_work(&prrn_work); + prrn_update_scope = scope; + schedule_work(&prrn_work); } +static void handle_rtas_event(const struct rtas_error_log *log) +{ + if (rtas_error_type(log) != RTAS_TYPE_PRRN || !prrn_is_enabled()) + return; + + /* For PRRN Events the extended log length is used to denote + * the scope for calling rtas update-nodes. + */ + prrn_schedule_update(rtas_error_extended_log_length(log)); +} + +#else + +static void handle_rtas_event(const struct rtas_error_log *log) +{ + return; +} + +#endif + static int rtas_log_open(struct inode * inode, struct file * file) { return 0; @@ -353,6 +399,7 @@ static const struct file_operations proc_rtas_log_operations = { .poll = rtas_log_poll, .open = rtas_log_open, .release = rtas_log_release, + .llseek = noop_llseek, }; static int enable_surveillance(int timeout) @@ -386,8 +433,10 @@ static void do_event_scan(void) break; } - if (error == 0) + if (error == 0) { pSeries_log_error(logdata, ERR_TYPE_RTAS_LOG, 0); + handle_rtas_event((struct rtas_error_log *)logdata); + } } while(error == 0); } @@ -410,9 +459,10 @@ static void rtas_event_scan(struct work_struct *w) get_online_cpus(); - cpu = next_cpu(smp_processor_id(), cpu_online_map); - if (cpu == NR_CPUS) { - cpu = first_cpu(cpu_online_map); + /* raw_ OK because just using CPU as starting point. */ + cpu = cpumask_next(raw_smp_processor_id(), cpu_online_mask); + if (cpu >= nr_cpu_ids) { + cpu = cpumask_first(cpu_online_mask); if (first_pass) { first_pass = 0; @@ -462,13 +512,20 @@ static void start_event_scan(void) pr_debug("rtasd: will sleep for %d milliseconds\n", (30000 / rtas_event_scan_rate)); - /* Retreive errors from nvram if any */ + /* Retrieve errors from nvram if any */ retreive_nvram_error_log(); - schedule_delayed_work_on(first_cpu(cpu_online_map), &event_scan_work, - event_scan_delay); + schedule_delayed_work_on(cpumask_first(cpu_online_mask), + &event_scan_work, event_scan_delay); } +/* Cancel the rtas event scan work */ +void rtas_cancel_event_scan(void) +{ + cancel_delayed_work_sync(&event_scan_work); +} +EXPORT_SYMBOL_GPL(rtas_cancel_event_scan); + static int __init rtas_init(void) { struct proc_dir_entry *entry; @@ -489,6 +546,12 @@ static int __init rtas_init(void) return -ENODEV; } + if (!rtas_event_scan_rate) { + /* Broken firmware: take a rate of zero to mean don't scan */ + printk(KERN_DEBUG "rtasd: scan rate is 0, not scanning\n"); + return 0; + } + /* Make room for the sequence number */ rtas_error_log_max = rtas_get_error_log_max(); rtas_error_log_buffer_max = rtas_error_log_max + sizeof(int); |
