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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.32-rc2 303 lines 7.4 kB view raw
1/* 2 * SN Platform system controller communication support 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file "COPYING" in the main directory of this archive 6 * for more details. 7 * 8 * Copyright (C) 2004-2006 Silicon Graphics, Inc. All rights reserved. 9 */ 10 11/* 12 * System controller event handler 13 * 14 * These routines deal with environmental events arriving from the 15 * system controllers. 16 */ 17 18#include <linux/interrupt.h> 19#include <linux/sched.h> 20#include <asm/byteorder.h> 21#include <asm/sn/sn_sal.h> 22#include <asm/unaligned.h> 23#include "snsc.h" 24 25static struct subch_data_s *event_sd; 26 27void scdrv_event(unsigned long); 28DECLARE_TASKLET(sn_sysctl_event, scdrv_event, 0); 29 30/* 31 * scdrv_event_interrupt 32 * 33 * Pull incoming environmental events off the physical link to the 34 * system controller and put them in a temporary holding area in SAL. 35 * Schedule scdrv_event() to move them along to their ultimate 36 * destination. 37 */ 38static irqreturn_t 39scdrv_event_interrupt(int irq, void *subch_data) 40{ 41 struct subch_data_s *sd = subch_data; 42 unsigned long flags; 43 int status; 44 45 spin_lock_irqsave(&sd->sd_rlock, flags); 46 status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); 47 48 if ((status > 0) && (status & SAL_IROUTER_INTR_RECV)) { 49 tasklet_schedule(&sn_sysctl_event); 50 } 51 spin_unlock_irqrestore(&sd->sd_rlock, flags); 52 return IRQ_HANDLED; 53} 54 55 56/* 57 * scdrv_parse_event 58 * 59 * Break an event (as read from SAL) into useful pieces so we can decide 60 * what to do with it. 61 */ 62static int 63scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc) 64{ 65 char *desc_end; 66 67 /* record event source address */ 68 *src = get_unaligned_be32(event); 69 event += 4; /* move on to event code */ 70 71 /* record the system controller's event code */ 72 *code = get_unaligned_be32(event); 73 event += 4; /* move on to event arguments */ 74 75 /* how many arguments are in the packet? */ 76 if (*event++ != 2) { 77 /* if not 2, give up */ 78 return -1; 79 } 80 81 /* parse out the ESP code */ 82 if (*event++ != IR_ARG_INT) { 83 /* not an integer argument, so give up */ 84 return -1; 85 } 86 *esp_code = get_unaligned_be32(event); 87 event += 4; 88 89 /* parse out the event description */ 90 if (*event++ != IR_ARG_ASCII) { 91 /* not an ASCII string, so give up */ 92 return -1; 93 } 94 event[CHUNKSIZE-1] = '\0'; /* ensure this string ends! */ 95 event += 2; /* skip leading CR/LF */ 96 desc_end = desc + sprintf(desc, "%s", event); 97 98 /* strip trailing CR/LF (if any) */ 99 for (desc_end--; 100 (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa)); 101 desc_end--) { 102 *desc_end = '\0'; 103 } 104 105 return 0; 106} 107 108 109/* 110 * scdrv_event_severity 111 * 112 * Figure out how urgent a message we should write to the console/syslog 113 * via printk. 114 */ 115static char * 116scdrv_event_severity(int code) 117{ 118 int ev_class = (code & EV_CLASS_MASK); 119 int ev_severity = (code & EV_SEVERITY_MASK); 120 char *pk_severity = KERN_NOTICE; 121 122 switch (ev_class) { 123 case EV_CLASS_POWER: 124 switch (ev_severity) { 125 case EV_SEVERITY_POWER_LOW_WARNING: 126 case EV_SEVERITY_POWER_HIGH_WARNING: 127 pk_severity = KERN_WARNING; 128 break; 129 case EV_SEVERITY_POWER_HIGH_FAULT: 130 case EV_SEVERITY_POWER_LOW_FAULT: 131 pk_severity = KERN_ALERT; 132 break; 133 } 134 break; 135 case EV_CLASS_FAN: 136 switch (ev_severity) { 137 case EV_SEVERITY_FAN_WARNING: 138 pk_severity = KERN_WARNING; 139 break; 140 case EV_SEVERITY_FAN_FAULT: 141 pk_severity = KERN_CRIT; 142 break; 143 } 144 break; 145 case EV_CLASS_TEMP: 146 switch (ev_severity) { 147 case EV_SEVERITY_TEMP_ADVISORY: 148 pk_severity = KERN_WARNING; 149 break; 150 case EV_SEVERITY_TEMP_CRITICAL: 151 pk_severity = KERN_CRIT; 152 break; 153 case EV_SEVERITY_TEMP_FAULT: 154 pk_severity = KERN_ALERT; 155 break; 156 } 157 break; 158 case EV_CLASS_ENV: 159 pk_severity = KERN_ALERT; 160 break; 161 case EV_CLASS_TEST_FAULT: 162 pk_severity = KERN_ALERT; 163 break; 164 case EV_CLASS_TEST_WARNING: 165 pk_severity = KERN_WARNING; 166 break; 167 case EV_CLASS_PWRD_NOTIFY: 168 pk_severity = KERN_ALERT; 169 break; 170 } 171 172 return pk_severity; 173} 174 175 176/* 177 * scdrv_dispatch_event 178 * 179 * Do the right thing with an incoming event. That's often nothing 180 * more than printing it to the system log. For power-down notifications 181 * we start a graceful shutdown. 182 */ 183static void 184scdrv_dispatch_event(char *event, int len) 185{ 186 static int snsc_shutting_down = 0; 187 int code, esp_code, src, class; 188 char desc[CHUNKSIZE]; 189 char *severity; 190 191 if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) { 192 /* ignore uninterpretible event */ 193 return; 194 } 195 196 /* how urgent is the message? */ 197 severity = scdrv_event_severity(code); 198 199 class = (code & EV_CLASS_MASK); 200 201 if (class == EV_CLASS_PWRD_NOTIFY || code == ENV_PWRDN_PEND) { 202 if (snsc_shutting_down) 203 return; 204 205 snsc_shutting_down = 1; 206 207 /* give a message for each type of event */ 208 if (class == EV_CLASS_PWRD_NOTIFY) 209 printk(KERN_NOTICE "Power off indication received." 210 " Sending SIGPWR to init...\n"); 211 else if (code == ENV_PWRDN_PEND) 212 printk(KERN_CRIT "WARNING: Shutting down the system" 213 " due to a critical environmental condition." 214 " Sending SIGPWR to init...\n"); 215 216 /* give a SIGPWR signal to init proc */ 217 kill_cad_pid(SIGPWR, 0); 218 } else { 219 /* print to system log */ 220 printk("%s|$(0x%x)%s\n", severity, esp_code, desc); 221 } 222} 223 224 225/* 226 * scdrv_event 227 * 228 * Called as a tasklet when an event arrives from the L1. Read the event 229 * from where it's temporarily stored in SAL and call scdrv_dispatch_event() 230 * to send it on its way. Keep trying to read events until SAL indicates 231 * that there are no more immediately available. 232 */ 233void 234scdrv_event(unsigned long dummy) 235{ 236 int status; 237 int len; 238 unsigned long flags; 239 struct subch_data_s *sd = event_sd; 240 241 /* anything to read? */ 242 len = CHUNKSIZE; 243 spin_lock_irqsave(&sd->sd_rlock, flags); 244 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 245 sd->sd_rb, &len); 246 247 while (!(status < 0)) { 248 spin_unlock_irqrestore(&sd->sd_rlock, flags); 249 scdrv_dispatch_event(sd->sd_rb, len); 250 len = CHUNKSIZE; 251 spin_lock_irqsave(&sd->sd_rlock, flags); 252 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 253 sd->sd_rb, &len); 254 } 255 spin_unlock_irqrestore(&sd->sd_rlock, flags); 256} 257 258 259/* 260 * scdrv_event_init 261 * 262 * Sets up a system controller subchannel to begin receiving event 263 * messages. This is sort of a specialized version of scdrv_open() 264 * in drivers/char/sn_sysctl.c. 265 */ 266void 267scdrv_event_init(struct sysctl_data_s *scd) 268{ 269 int rv; 270 271 event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); 272 if (event_sd == NULL) { 273 printk(KERN_WARNING "%s: couldn't allocate subchannel info" 274 " for event monitoring\n", __func__); 275 return; 276 } 277 278 /* initialize subch_data_s fields */ 279 event_sd->sd_nasid = scd->scd_nasid; 280 spin_lock_init(&event_sd->sd_rlock); 281 282 /* ask the system controllers to send events to this node */ 283 event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid); 284 285 if (event_sd->sd_subch < 0) { 286 kfree(event_sd); 287 printk(KERN_WARNING "%s: couldn't open event subchannel\n", 288 __func__); 289 return; 290 } 291 292 /* hook event subchannel up to the system controller interrupt */ 293 rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, 294 IRQF_SHARED | IRQF_DISABLED, 295 "system controller events", event_sd); 296 if (rv) { 297 printk(KERN_WARNING "%s: irq request failed (%d)\n", 298 __func__, rv); 299 ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); 300 kfree(event_sd); 301 return; 302 } 303}