Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/le: Enable RTAS events support

The current kernel code assumes big endian and parses RTAS events all
wrong. The most visible effect is that we cannot honor EPOW events,
meaning, for example, we cannot shut down a guest properly from the
hypervisor.

This new patch is largely inspired by Nathan's work: we get rid of all
the bit fields in the RTAS event structures (even the unused ones, for
consistency). We also introduce endian safe accessors for the fields used
by the kernel (trivial rtas_error_type() accessor added for consistency).

Cc: Nathan Fontenot <nfont@linux.vnet.ibm.com>
Signed-off-by: Greg Kurz <gkurz@linux.vnet.ibm.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

authored by

Greg Kurz and committed by
Benjamin Herrenschmidt
a08a53ea c59c015b

+128 -61
+94 -33
arch/powerpc/include/asm/rtas.h
··· 150 150 #define RTAS_VECTOR_EXTERNAL_INTERRUPT 0x500 151 151 152 152 struct rtas_error_log { 153 - unsigned long version:8; /* Architectural version */ 154 - unsigned long severity:3; /* Severity level of error */ 155 - unsigned long disposition:2; /* Degree of recovery */ 156 - unsigned long extended:1; /* extended log present? */ 157 - unsigned long /* reserved */ :2; /* Reserved for future use */ 158 - unsigned long initiator:4; /* Initiator of event */ 159 - unsigned long target:4; /* Target of failed operation */ 160 - unsigned long type:8; /* General event or error*/ 161 - unsigned long extended_log_length:32; /* length in bytes */ 162 - unsigned char buffer[1]; /* Start of extended log */ 153 + /* Byte 0 */ 154 + uint8_t byte0; /* Architectural version */ 155 + 156 + /* Byte 1 */ 157 + uint8_t byte1; 158 + /* XXXXXXXX 159 + * XXX 3: Severity level of error 160 + * XX 2: Degree of recovery 161 + * X 1: Extended log present? 162 + * XX 2: Reserved 163 + */ 164 + 165 + /* Byte 2 */ 166 + uint8_t byte2; 167 + /* XXXXXXXX 168 + * XXXX 4: Initiator of event 169 + * XXXX 4: Target of failed operation 170 + */ 171 + uint8_t byte3; /* General event or error*/ 172 + __be32 extended_log_length; /* length in bytes */ 173 + unsigned char buffer[1]; /* Start of extended log */ 163 174 /* Variable length. */ 164 175 }; 176 + 177 + static inline uint8_t rtas_error_severity(const struct rtas_error_log *elog) 178 + { 179 + return (elog->byte1 & 0xE0) >> 5; 180 + } 181 + 182 + static inline uint8_t rtas_error_disposition(const struct rtas_error_log *elog) 183 + { 184 + return (elog->byte1 & 0x18) >> 3; 185 + } 186 + 187 + static inline uint8_t rtas_error_extended(const struct rtas_error_log *elog) 188 + { 189 + return (elog->byte1 & 0x04) >> 2; 190 + } 191 + 192 + #define rtas_error_type(x) ((x)->byte3) 193 + 194 + static inline 195 + uint32_t rtas_error_extended_log_length(const struct rtas_error_log *elog) 196 + { 197 + return be32_to_cpu(elog->extended_log_length); 198 + } 165 199 166 200 #define RTAS_V6EXT_LOG_FORMAT_EVENT_LOG 14 167 201 ··· 206 172 */ 207 173 struct rtas_ext_event_log_v6 { 208 174 /* Byte 0 */ 209 - uint32_t log_valid:1; /* 1:Log valid */ 210 - uint32_t unrecoverable_error:1; /* 1:Unrecoverable error */ 211 - uint32_t recoverable_error:1; /* 1:recoverable (correctable */ 212 - /* or successfully retried) */ 213 - uint32_t degraded_operation:1; /* 1:Unrecoverable err, bypassed*/ 214 - /* - degraded operation (e.g. */ 215 - /* CPU or mem taken off-line) */ 216 - uint32_t predictive_error:1; 217 - uint32_t new_log:1; /* 1:"New" log (Always 1 for */ 218 - /* data returned from RTAS */ 219 - uint32_t big_endian:1; /* 1: Big endian */ 220 - uint32_t :1; /* reserved */ 175 + uint8_t byte0; 176 + /* XXXXXXXX 177 + * X 1: Log valid 178 + * X 1: Unrecoverable error 179 + * X 1: Recoverable (correctable or successfully retried) 180 + * X 1: Bypassed unrecoverable error (degraded operation) 181 + * X 1: Predictive error 182 + * X 1: "New" log (always 1 for data returned from RTAS) 183 + * X 1: Big Endian 184 + * X 1: Reserved 185 + */ 186 + 221 187 /* Byte 1 */ 222 - uint32_t :8; /* reserved */ 188 + uint8_t byte1; /* reserved */ 189 + 223 190 /* Byte 2 */ 224 - uint32_t powerpc_format:1; /* Set to 1 (indicating log is */ 225 - /* in PowerPC format */ 226 - uint32_t :3; /* reserved */ 227 - uint32_t log_format:4; /* Log format indicator. Define */ 228 - /* format used for byte 12-2047 */ 191 + uint8_t byte2; 192 + /* XXXXXXXX 193 + * X 1: Set to 1 (indicating log is in PowerPC format) 194 + * XXX 3: Reserved 195 + * XXXX 4: Log format used for bytes 12-2047 196 + */ 197 + 229 198 /* Byte 3 */ 230 - uint32_t :8; /* reserved */ 199 + uint8_t byte3; /* reserved */ 231 200 /* Byte 4-11 */ 232 201 uint8_t reserved[8]; /* reserved */ 233 202 /* Byte 12-15 */ 234 - uint32_t company_id; /* Company ID of the company */ 203 + __be32 company_id; /* Company ID of the company */ 235 204 /* that defines the format for */ 236 205 /* the vendor specific log type */ 237 206 /* Byte 16-end of log */ 238 207 uint8_t vendor_log[1]; /* Start of vendor specific log */ 239 208 /* Variable length. */ 240 209 }; 210 + 211 + static 212 + inline uint8_t rtas_ext_event_log_format(struct rtas_ext_event_log_v6 *ext_log) 213 + { 214 + return ext_log->byte2 & 0x0F; 215 + } 216 + 217 + static 218 + inline uint32_t rtas_ext_event_company_id(struct rtas_ext_event_log_v6 *ext_log) 219 + { 220 + return be32_to_cpu(ext_log->company_id); 221 + } 241 222 242 223 /* pSeries event log format */ 243 224 ··· 276 227 277 228 /* Vendor specific Platform Event Log Format, Version 6, section header */ 278 229 struct pseries_errorlog { 279 - uint16_t id; /* 0x00 2-byte ASCII section ID */ 280 - uint16_t length; /* 0x02 Section length in bytes */ 230 + __be16 id; /* 0x00 2-byte ASCII section ID */ 231 + __be16 length; /* 0x02 Section length in bytes */ 281 232 uint8_t version; /* 0x04 Section version */ 282 233 uint8_t subtype; /* 0x05 Section subtype */ 283 - uint16_t creator_component; /* 0x06 Creator component ID */ 234 + __be16 creator_component; /* 0x06 Creator component ID */ 284 235 uint8_t data[]; /* 0x08 Start of section data */ 285 236 }; 237 + 238 + static 239 + inline uint16_t pseries_errorlog_id(struct pseries_errorlog *sect) 240 + { 241 + return be16_to_cpu(sect->id); 242 + } 243 + 244 + static 245 + inline uint16_t pseries_errorlog_length(struct pseries_errorlog *sect) 246 + { 247 + return be16_to_cpu(sect->length); 248 + } 286 249 287 250 struct pseries_errorlog *get_pseries_errorlog(struct rtas_error_log *log, 288 251 uint16_t section_id);
+9 -6
arch/powerpc/kernel/rtas.c
··· 993 993 (struct rtas_ext_event_log_v6 *)log->buffer; 994 994 struct pseries_errorlog *sect; 995 995 unsigned char *p, *log_end; 996 + uint32_t ext_log_length = rtas_error_extended_log_length(log); 997 + uint8_t log_format = rtas_ext_event_log_format(ext_log); 998 + uint32_t company_id = rtas_ext_event_company_id(ext_log); 996 999 997 1000 /* Check that we understand the format */ 998 - if (log->extended_log_length < sizeof(struct rtas_ext_event_log_v6) || 999 - ext_log->log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG || 1000 - ext_log->company_id != RTAS_V6EXT_COMPANY_ID_IBM) 1001 + if (ext_log_length < sizeof(struct rtas_ext_event_log_v6) || 1002 + log_format != RTAS_V6EXT_LOG_FORMAT_EVENT_LOG || 1003 + company_id != RTAS_V6EXT_COMPANY_ID_IBM) 1001 1004 return NULL; 1002 1005 1003 - log_end = log->buffer + log->extended_log_length; 1006 + log_end = log->buffer + ext_log_length; 1004 1007 p = ext_log->vendor_log; 1005 1008 1006 1009 while (p < log_end) { 1007 1010 sect = (struct pseries_errorlog *)p; 1008 - if (sect->id == section_id) 1011 + if (pseries_errorlog_id(sect) == section_id) 1009 1012 return sect; 1010 - p += sect->length; 1013 + p += pseries_errorlog_length(sect); 1011 1014 } 1012 1015 1013 1016 return NULL;
+12 -12
arch/powerpc/kernel/rtasd.c
··· 150 150 struct rtas_error_log *errlog = (struct rtas_error_log *)buf; 151 151 152 152 printk(RTAS_DEBUG "event: %d, Type: %s, Severity: %d\n", 153 - error_log_cnt, rtas_event_type(errlog->type), 154 - errlog->severity); 153 + error_log_cnt, rtas_event_type(rtas_error_type(errlog)), 154 + rtas_error_severity(errlog)); 155 155 } 156 156 } 157 157 ··· 159 159 { 160 160 int len; 161 161 struct rtas_error_log *err; 162 + uint32_t extended_log_length; 162 163 163 164 /* rtas fixed header */ 164 165 len = 8; 165 166 err = (struct rtas_error_log *)buf; 166 - if (err->extended && err->extended_log_length) { 167 + extended_log_length = rtas_error_extended_log_length(err); 168 + if (rtas_error_extended(err) && extended_log_length) { 167 169 168 170 /* extended header */ 169 - len += err->extended_log_length; 171 + len += extended_log_length; 170 172 } 171 173 172 174 if (rtas_error_log_max == 0) ··· 295 293 296 294 static void handle_rtas_event(const struct rtas_error_log *log) 297 295 { 298 - if (log->type == RTAS_TYPE_PRRN) { 299 - /* For PRRN Events the extended log length is used to denote 300 - * the scope for calling rtas update-nodes. 301 - */ 302 - if (prrn_is_enabled()) 303 - prrn_schedule_update(log->extended_log_length); 304 - } 296 + if (rtas_error_type(log) != RTAS_TYPE_PRRN || !prrn_is_enabled()) 297 + return; 305 298 306 - return; 299 + /* For PRRN Events the extended log length is used to denote 300 + * the scope for calling rtas update-nodes. 301 + */ 302 + prrn_schedule_update(rtas_error_extended_log_length(log)); 307 303 } 308 304 309 305 #else
+3 -3
arch/powerpc/platforms/pseries/io_event_irq.c
··· 82 82 * RTAS_TYPE_IO only exists in extended event log version 6 or later. 83 83 * No need to check event log version. 84 84 */ 85 - if (unlikely(elog->type != RTAS_TYPE_IO)) { 86 - printk_once(KERN_WARNING "io_event_irq: Unexpected event type %d", 87 - elog->type); 85 + if (unlikely(rtas_error_type(elog) != RTAS_TYPE_IO)) { 86 + printk_once(KERN_WARNING"io_event_irq: Unexpected event type %d", 87 + rtas_error_type(elog)); 88 88 return NULL; 89 89 } 90 90
+10 -7
arch/powerpc/platforms/pseries/ras.c
··· 236 236 237 237 rtas_elog = (struct rtas_error_log *)ras_log_buf; 238 238 239 - if ((status == 0) && (rtas_elog->severity >= RTAS_SEVERITY_ERROR_SYNC)) 239 + if (status == 0 && 240 + rtas_error_severity(rtas_elog) >= RTAS_SEVERITY_ERROR_SYNC) 240 241 fatal = 1; 241 242 else 242 243 fatal = 0; ··· 301 300 302 301 /* If it isn't an extended log we can use the per cpu 64bit buffer */ 303 302 h = (struct rtas_error_log *)&savep[1]; 304 - if (!h->extended) { 303 + if (!rtas_error_extended(h)) { 305 304 memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64)); 306 305 errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf); 307 306 } else { 308 - int len; 307 + int len, error_log_length; 309 308 310 - len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX); 309 + error_log_length = 8 + rtas_error_extended_log_length(h); 310 + len = max_t(int, error_log_length, RTAS_ERROR_LOG_MAX); 311 311 memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX); 312 312 memcpy(global_mce_data_buf, h, len); 313 313 errhdr = (struct rtas_error_log *)global_mce_data_buf; ··· 352 350 static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err) 353 351 { 354 352 int recovered = 0; 353 + int disposition = rtas_error_disposition(err); 355 354 356 355 if (!(regs->msr & MSR_RI)) { 357 356 /* If MSR_RI isn't set, we cannot recover */ 358 357 recovered = 0; 359 358 360 - } else if (err->disposition == RTAS_DISP_FULLY_RECOVERED) { 359 + } else if (disposition == RTAS_DISP_FULLY_RECOVERED) { 361 360 /* Platform corrected itself */ 362 361 recovered = 1; 363 362 364 - } else if (err->disposition == RTAS_DISP_LIMITED_RECOVERY) { 363 + } else if (disposition == RTAS_DISP_LIMITED_RECOVERY) { 365 364 /* Platform corrected itself but could be degraded */ 366 365 printk(KERN_ERR "MCE: limited recovery, system may " 367 366 "be degraded\n"); 368 367 recovered = 1; 369 368 370 369 } else if (user_mode(regs) && !is_global_init(current) && 371 - err->severity == RTAS_SEVERITY_ERROR_SYNC) { 370 + rtas_error_severity(err) == RTAS_SEVERITY_ERROR_SYNC) { 372 371 373 372 /* 374 373 * If we received a synchronous error when in userspace