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.19-rc1 309 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, struct pt_regs *regs) 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 struct task_struct *p; 207 208 if (snsc_shutting_down) 209 return; 210 211 snsc_shutting_down = 1; 212 213 /* give a message for each type of event */ 214 if (class == EV_CLASS_PWRD_NOTIFY) 215 printk(KERN_NOTICE "Power off indication received." 216 " Sending SIGPWR to init...\n"); 217 else if (code == ENV_PWRDN_PEND) 218 printk(KERN_CRIT "WARNING: Shutting down the system" 219 " due to a critical environmental condition." 220 " Sending SIGPWR to init...\n"); 221 222 /* give a SIGPWR signal to init proc */ 223 kill_cad_pid(SIGPWR, 0); 224 } else { 225 /* print to system log */ 226 printk("%s|$(0x%x)%s\n", severity, esp_code, desc); 227 } 228} 229 230 231/* 232 * scdrv_event 233 * 234 * Called as a tasklet when an event arrives from the L1. Read the event 235 * from where it's temporarily stored in SAL and call scdrv_dispatch_event() 236 * to send it on its way. Keep trying to read events until SAL indicates 237 * that there are no more immediately available. 238 */ 239void 240scdrv_event(unsigned long dummy) 241{ 242 int status; 243 int len; 244 unsigned long flags; 245 struct subch_data_s *sd = event_sd; 246 247 /* anything to read? */ 248 len = CHUNKSIZE; 249 spin_lock_irqsave(&sd->sd_rlock, flags); 250 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 251 sd->sd_rb, &len); 252 253 while (!(status < 0)) { 254 spin_unlock_irqrestore(&sd->sd_rlock, flags); 255 scdrv_dispatch_event(sd->sd_rb, len); 256 len = CHUNKSIZE; 257 spin_lock_irqsave(&sd->sd_rlock, flags); 258 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 259 sd->sd_rb, &len); 260 } 261 spin_unlock_irqrestore(&sd->sd_rlock, flags); 262} 263 264 265/* 266 * scdrv_event_init 267 * 268 * Sets up a system controller subchannel to begin receiving event 269 * messages. This is sort of a specialized version of scdrv_open() 270 * in drivers/char/sn_sysctl.c. 271 */ 272void 273scdrv_event_init(struct sysctl_data_s *scd) 274{ 275 int rv; 276 277 event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); 278 if (event_sd == NULL) { 279 printk(KERN_WARNING "%s: couldn't allocate subchannel info" 280 " for event monitoring\n", __FUNCTION__); 281 return; 282 } 283 284 /* initialize subch_data_s fields */ 285 event_sd->sd_nasid = scd->scd_nasid; 286 spin_lock_init(&event_sd->sd_rlock); 287 288 /* ask the system controllers to send events to this node */ 289 event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid); 290 291 if (event_sd->sd_subch < 0) { 292 kfree(event_sd); 293 printk(KERN_WARNING "%s: couldn't open event subchannel\n", 294 __FUNCTION__); 295 return; 296 } 297 298 /* hook event subchannel up to the system controller interrupt */ 299 rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, 300 IRQF_SHARED | IRQF_DISABLED, 301 "system controller events", event_sd); 302 if (rv) { 303 printk(KERN_WARNING "%s: irq request failed (%d)\n", 304 __FUNCTION__, rv); 305 ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); 306 kfree(event_sd); 307 return; 308 } 309}