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.14-rc3 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 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 int code, esp_code, src; 191 char desc[CHUNKSIZE]; 192 char *severity; 193 194 if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) { 195 /* ignore uninterpretible event */ 196 return; 197 } 198 199 /* how urgent is the message? */ 200 severity = scdrv_event_severity(code); 201 202 if ((code & EV_CLASS_MASK) == EV_CLASS_PWRD_NOTIFY) { 203 struct task_struct *p; 204 205 /* give a SIGPWR signal to init proc */ 206 207 /* first find init's task */ 208 read_lock(&tasklist_lock); 209 for_each_process(p) { 210 if (p->pid == 1) 211 break; 212 } 213 if (p) { /* we found init's task */ 214 printk(KERN_EMERG "Power off indication received. Initiating power fail sequence...\n"); 215 force_sig(SIGPWR, p); 216 } else { /* failed to find init's task - just give message(s) */ 217 printk(KERN_WARNING "Failed to find init proc to handle power off!\n"); 218 printk("%s|$(0x%x)%s\n", severity, esp_code, desc); 219 } 220 read_unlock(&tasklist_lock); 221 } else { 222 /* print to system log */ 223 printk("%s|$(0x%x)%s\n", severity, esp_code, desc); 224 } 225} 226 227 228/* 229 * scdrv_event 230 * 231 * Called as a tasklet when an event arrives from the L1. Read the event 232 * from where it's temporarily stored in SAL and call scdrv_dispatch_event() 233 * to send it on its way. Keep trying to read events until SAL indicates 234 * that there are no more immediately available. 235 */ 236void 237scdrv_event(unsigned long dummy) 238{ 239 int status; 240 int len; 241 unsigned long flags; 242 struct subch_data_s *sd = event_sd; 243 244 /* anything to read? */ 245 len = CHUNKSIZE; 246 spin_lock_irqsave(&sd->sd_rlock, flags); 247 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 248 sd->sd_rb, &len); 249 250 while (!(status < 0)) { 251 spin_unlock_irqrestore(&sd->sd_rlock, flags); 252 scdrv_dispatch_event(sd->sd_rb, len); 253 len = CHUNKSIZE; 254 spin_lock_irqsave(&sd->sd_rlock, flags); 255 status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, 256 sd->sd_rb, &len); 257 } 258 spin_unlock_irqrestore(&sd->sd_rlock, flags); 259} 260 261 262/* 263 * scdrv_event_init 264 * 265 * Sets up a system controller subchannel to begin receiving event 266 * messages. This is sort of a specialized version of scdrv_open() 267 * in drivers/char/sn_sysctl.c. 268 */ 269void 270scdrv_event_init(struct sysctl_data_s *scd) 271{ 272 int rv; 273 274 event_sd = kmalloc(sizeof (struct subch_data_s), GFP_KERNEL); 275 if (event_sd == NULL) { 276 printk(KERN_WARNING "%s: couldn't allocate subchannel info" 277 " for event monitoring\n", __FUNCTION__); 278 return; 279 } 280 281 /* initialize subch_data_s fields */ 282 memset(event_sd, 0, sizeof (struct subch_data_s)); 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 SA_SHIRQ | SA_INTERRUPT, 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} 308 309