snsc_event.c 7.3 KB

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