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