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.22-rc1 307 lines 7.6 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 <linux/byteorder/generic.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 __be32 from_buf; 67 68 /* record event source address */ 69 from_buf = get_unaligned((__be32 *)event); 70 *src = be32_to_cpup(&from_buf); 71 event += 4; /* move on to event code */ 72 73 /* record the system controller's event code */ 74 from_buf = get_unaligned((__be32 *)event); 75 *code = be32_to_cpup(&from_buf); 76 event += 4; /* move on to event arguments */ 77 78 /* how many arguments are in the packet? */ 79 if (*event++ != 2) { 80 /* if not 2, give up */ 81 return -1; 82 } 83 84 /* parse out the ESP code */ 85 if (*event++ != IR_ARG_INT) { 86 /* not an integer argument, so give up */ 87 return -1; 88 } 89 from_buf = get_unaligned((__be32 *)event); 90 *esp_code = be32_to_cpup(&from_buf); 91 event += 4; 92 93 /* parse out the event description */ 94 if (*event++ != IR_ARG_ASCII) { 95 /* not an ASCII string, so give up */ 96 return -1; 97 } 98 event[CHUNKSIZE-1] = '\0'; /* ensure this string ends! */ 99 event += 2; /* skip leading CR/LF */ 100 desc_end = desc + sprintf(desc, "%s", event); 101 102 /* strip trailing CR/LF (if any) */ 103 for (desc_end--; 104 (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa)); 105 desc_end--) { 106 *desc_end = '\0'; 107 } 108 109 return 0; 110} 111 112 113/* 114 * scdrv_event_severity 115 * 116 * Figure out how urgent a message we should write to the console/syslog 117 * via printk. 118 */ 119static char * 120scdrv_event_severity(int code) 121{ 122 int ev_class = (code & EV_CLASS_MASK); 123 int ev_severity = (code & EV_SEVERITY_MASK); 124 char *pk_severity = KERN_NOTICE; 125 126 switch (ev_class) { 127 case EV_CLASS_POWER: 128 switch (ev_severity) { 129 case EV_SEVERITY_POWER_LOW_WARNING: 130 case EV_SEVERITY_POWER_HIGH_WARNING: 131 pk_severity = KERN_WARNING; 132 break; 133 case EV_SEVERITY_POWER_HIGH_FAULT: 134 case EV_SEVERITY_POWER_LOW_FAULT: 135 pk_severity = KERN_ALERT; 136 break; 137 } 138 break; 139 case EV_CLASS_FAN: 140 switch (ev_severity) { 141 case EV_SEVERITY_FAN_WARNING: 142 pk_severity = KERN_WARNING; 143 break; 144 case EV_SEVERITY_FAN_FAULT: 145 pk_severity = KERN_CRIT; 146 break; 147 } 148 break; 149 case EV_CLASS_TEMP: 150 switch (ev_severity) { 151 case EV_SEVERITY_TEMP_ADVISORY: 152 pk_severity = KERN_WARNING; 153 break; 154 case EV_SEVERITY_TEMP_CRITICAL: 155 pk_severity = KERN_CRIT; 156 break; 157 case EV_SEVERITY_TEMP_FAULT: 158 pk_severity = KERN_ALERT; 159 break; 160 } 161 break; 162 case EV_CLASS_ENV: 163 pk_severity = KERN_ALERT; 164 break; 165 case EV_CLASS_TEST_FAULT: 166 pk_severity = KERN_ALERT; 167 break; 168 case EV_CLASS_TEST_WARNING: 169 pk_severity = KERN_WARNING; 170 break; 171 case EV_CLASS_PWRD_NOTIFY: 172 pk_severity = KERN_ALERT; 173 break; 174 } 175 176 return pk_severity; 177} 178 179 180/* 181 * scdrv_dispatch_event 182 * 183 * Do the right thing with an incoming event. That's often nothing 184 * more than printing it to the system log. For power-down notifications 185 * we start a graceful shutdown. 186 */ 187static void 188scdrv_dispatch_event(char *event, int len) 189{ 190 static int snsc_shutting_down = 0; 191 int code, esp_code, src, class; 192 char desc[CHUNKSIZE]; 193 char *severity; 194 195 if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) { 196 /* ignore uninterpretible event */ 197 return; 198 } 199 200 /* how urgent is the message? */ 201 severity = scdrv_event_severity(code); 202 203 class = (code & EV_CLASS_MASK); 204 205 if (class == EV_CLASS_PWRD_NOTIFY || code == ENV_PWRDN_PEND) { 206 if (snsc_shutting_down) 207 return; 208 209 snsc_shutting_down = 1; 210 211 /* give a message for each type of event */ 212 if (class == EV_CLASS_PWRD_NOTIFY) 213 printk(KERN_NOTICE "Power off indication received." 214 " Sending SIGPWR to init...\n"); 215 else if (code == ENV_PWRDN_PEND) 216 printk(KERN_CRIT "WARNING: Shutting down the system" 217 " due to a critical environmental condition." 218 " Sending SIGPWR to init...\n"); 219 220 /* give a SIGPWR signal to init proc */ 221 kill_cad_pid(SIGPWR, 0); 222 } else { 223 /* print to system log */ 224 printk("%s|$(0x%x)%s\n", severity, esp_code, desc); 225 } 226} 227 228 229/* 230 * scdrv_event 231 * 232 * Called as a tasklet when an event arrives from the L1. Read the event 233 * from where it's temporarily stored in SAL and call scdrv_dispatch_event() 234 * to send it on its way. Keep trying to read events until SAL indicates 235 * that there are no more immediately available. 236 */ 237void 238scdrv_event(unsigned long dummy) 239{ 240 int status; 241 int len; 242 unsigned long flags; 243 struct subch_data_s *sd = event_sd; 244 245 /* anything to read? */ 246 len = CHUNKSIZE; 247 spin_lock_irqsave(&sd->sd_rlock, flags); 248 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 249 sd->sd_rb, &len); 250 251 while (!(status < 0)) { 252 spin_unlock_irqrestore(&sd->sd_rlock, flags); 253 scdrv_dispatch_event(sd->sd_rb, len); 254 len = CHUNKSIZE; 255 spin_lock_irqsave(&sd->sd_rlock, flags); 256 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 257 sd->sd_rb, &len); 258 } 259 spin_unlock_irqrestore(&sd->sd_rlock, flags); 260} 261 262 263/* 264 * scdrv_event_init 265 * 266 * Sets up a system controller subchannel to begin receiving event 267 * messages. This is sort of a specialized version of scdrv_open() 268 * in drivers/char/sn_sysctl.c. 269 */ 270void 271scdrv_event_init(struct sysctl_data_s *scd) 272{ 273 int rv; 274 275 event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); 276 if (event_sd == NULL) { 277 printk(KERN_WARNING "%s: couldn't allocate subchannel info" 278 " for event monitoring\n", __FUNCTION__); 279 return; 280 } 281 282 /* initialize subch_data_s fields */ 283 event_sd->sd_nasid = scd->scd_nasid; 284 spin_lock_init(&event_sd->sd_rlock); 285 286 /* ask the system controllers to send events to this node */ 287 event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid); 288 289 if (event_sd->sd_subch < 0) { 290 kfree(event_sd); 291 printk(KERN_WARNING "%s: couldn't open event subchannel\n", 292 __FUNCTION__); 293 return; 294 } 295 296 /* hook event subchannel up to the system controller interrupt */ 297 rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, 298 IRQF_SHARED | IRQF_DISABLED, 299 "system controller events", event_sd); 300 if (rv) { 301 printk(KERN_WARNING "%s: irq request failed (%d)\n", 302 __FUNCTION__, rv); 303 ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); 304 kfree(event_sd); 305 return; 306 } 307}