clean
[linux-2.4.21-pre4.git] / drivers / s390 / s390mach.c
1 /*
2  *  arch/s390/kernel/s390mach.c
3  *   S/390 machine check handler,
4  *            currently only channel-reports are supported
5  *
6  *  S390 version
7  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
8  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
9  */
10
11 #include <linux/config.h>
12 #include <linux/spinlock.h>
13 #include <linux/init.h>
14 #include <linux/slab.h>
15 #ifdef CONFIG_SMP
16 #include <linux/smp.h>
17 #endif
18
19 #include <asm/irq.h>
20 #include <asm/lowcore.h>
21 #include <asm/semaphore.h>
22 #include <asm/s390io.h>
23 #include <asm/s390dyn.h>
24 #include <asm/s390mach.h>
25 #ifdef CONFIG_MACHCHK_WARNING
26 #include <asm/signal.h>
27 #endif
28
29 extern void ctrl_alt_del(void);
30
31 #define S390_MACHCHK_DEBUG
32
33 static int         s390_machine_check_handler( void * parm );
34 static void        s390_enqueue_mchchk( mache_t *mchchk );
35 static mache_t    *s390_dequeue_mchchk( void );
36 static void        s390_enqueue_free_mchchk( mache_t *mchchk );
37 static mache_t    *s390_dequeue_free_mchchk( void );
38 static int         s390_collect_crw_info( void );
39 #ifdef CONFIG_MACHCHK_WARNING
40 static int         s390_post_warning( void );
41 #endif
42
43 static mache_t    *mchchk_queue_head = NULL;
44 static mache_t    *mchchk_queue_tail = NULL;
45 static mache_t    *mchchk_queue_free = NULL;
46 static crwe_t     *crw_buffer_anchor = NULL;
47 static spinlock_t  mchchk_queue_lock = SPIN_LOCK_UNLOCKED;
48 static spinlock_t  crw_queue_lock    = SPIN_LOCK_UNLOCKED;
49
50 static struct semaphore s_sem;
51
52 #ifdef CONFIG_MACHCHK_WARNING
53 static int mchchk_wng_posted = 0;
54 #endif
55
56 /*
57  * s390_init_machine_check
58  *
59  * initialize machine check handling
60  */
61 void s390_init_machine_check( void )
62 {
63         crwe_t  *pcrwe;  /* CRW buffer element pointer */
64         mache_t *pmache;   /* machine check element pointer */
65
66         init_MUTEX_LOCKED( &s_sem );
67
68         pcrwe = kmalloc( MAX_CRW_PENDING * sizeof( crwe_t), GFP_KERNEL);
69
70         if ( pcrwe )
71         {
72                 int i;
73
74                 crw_buffer_anchor = pcrwe;
75
76                 for ( i=0; i < MAX_CRW_PENDING-1; i++)
77                 {
78                         pcrwe->crwe_next = (crwe_t *)((unsigned long)pcrwe + sizeof(crwe_t));
79                 pcrwe            = pcrwe->crwe_next;
80
81                 } /* endfor */  
82
83                 pcrwe->crwe_next = NULL;
84
85         }
86         else
87         {
88                 panic( "s390_init_machine_check : unable to obtain memory\n");          
89
90         } /* endif */
91
92         pmache = kmalloc( MAX_MACH_PENDING * sizeof( mache_t), GFP_KERNEL);
93
94         if ( pmache )
95         {
96                 int i;
97
98                 for ( i=0; i < MAX_MACH_PENDING; i++)
99                 {
100                         s390_enqueue_free_mchchk( pmache );
101                    pmache = (mache_t *)((unsigned long)pmache + sizeof(mache_t));
102
103                 } /* endfor */  
104         }
105         else
106         {
107                 panic( "s390_init_machine_check : unable to obtain memory\n");          
108
109         } /* endif */
110
111 #ifdef S390_MACHCHK_DEBUG
112         printk( KERN_NOTICE "init_mach : starting machine check handler\n");
113 #endif  
114
115         kernel_thread( s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES);
116
117         ctl_clear_bit( 14, 25 );  // disable damage MCH         
118
119         ctl_set_bit( 14, 26 ); /* enable degradation MCH */
120         ctl_set_bit( 14, 27 ); /* enable system recovery MCH */
121 #if 1
122         ctl_set_bit( 14, 28 );          // enable channel report MCH
123 #endif
124 #ifdef CONFIG_MACHCK_WARNING
125         ctl_set_bit( 14, 24);   /* enable warning MCH */
126 #endif
127
128 #ifdef S390_MACHCHK_DEBUG
129         printk( KERN_DEBUG "init_mach : machine check buffer : head = %08X\n",
130             (unsigned)&mchchk_queue_head);
131         printk( KERN_DEBUG "init_mach : machine check buffer : tail = %08X\n",
132             (unsigned)&mchchk_queue_tail);
133         printk( KERN_DEBUG "init_mach : machine check buffer : free = %08X\n",
134             (unsigned)&mchchk_queue_free);
135         printk( KERN_DEBUG "init_mach : CRW entry buffer anchor = %08X\n",
136             (unsigned)&crw_buffer_anchor);
137         printk( KERN_DEBUG "init_mach : machine check handler ready\n");
138 #endif  
139
140         return;
141 }
142
143 static void s390_handle_damage(char * msg){
144
145         unsigned long caller = (unsigned long) __builtin_return_address(0);
146
147         printk(KERN_EMERG "%s\n", msg);
148 #ifdef CONFIG_SMP
149         smp_send_stop();
150 #endif
151         disabled_wait(caller);
152         return;
153 }
154
155
156 /*
157  * s390_do_machine_check
158  *
159  * mchine check pre-processor, collecting the machine check info,
160  *  queueing it and posting the machine check handler for processing.
161  */
162 void s390_do_machine_check( void )
163 {
164         int      crw_count;
165         mcic_t   mcic;
166
167 #ifdef S390_MACHCHK_DEBUG
168         printk( KERN_INFO "s390_do_machine_check : starting ...\n");
169 #endif
170
171         memcpy( &mcic,
172                 &S390_lowcore.mcck_interruption_code,
173                 sizeof(__u64));
174                 
175         if (mcic.mcc.mcd.sd) /* system damage */
176                 s390_handle_damage("received system damage machine check\n");
177
178         if (mcic.mcc.mcd.pd) /* instruction processing damage */
179                 s390_handle_damage("received instruction processing damage machine check\n");
180
181         if (mcic.mcc.mcd.se) /* storage error uncorrected */
182                 s390_handle_damage("received storage error uncorrected machine check\n");
183
184         if (mcic.mcc.mcd.sc) /* storage error corrected */
185                 printk(KERN_WARNING "received storage error corrected machine check\n");
186
187         if (mcic.mcc.mcd.ke) /* storage key-error uncorrected */
188                 s390_handle_damage("received storage key-error uncorrected machine check\n");
189
190         if (mcic.mcc.mcd.ds && mcic.mcc.mcd.fa) /* storage degradation */
191                 s390_handle_damage("received storage degradation machine check\n");
192
193         if ( mcic.mcc.mcd.cp )  // CRW pending ?
194         {
195                 crw_count = s390_collect_crw_info();
196
197                 if ( crw_count )
198                 {
199                         up( &s_sem );
200
201                 } /* endif */
202
203         } /* endif */
204 #ifdef CONFIG_MACHCHK_WARNING
205 /*
206  * The warning may remain for a prolonged period on the bare iron.
207  * (actually till the machine is powered off, or until the problem is gone)
208  * So we just stop listening for the WARNING MCH and prevent continuously
209  * being interrupted.  One caveat is however, that we must do this per 
210  * processor and cannot use the smp version of ctl_clear_bit().
211  * On VM we only get one interrupt per virtally presented machinecheck.
212  * Though one suffices, we may get one interrupt per (virtual) processor. 
213  */
214         if ( mcic.mcc.mcd.w )   // WARNING pending ?
215         {
216                 // Use single machine clear, as we cannot handle smp right now
217                 __ctl_clear_bit( 14, 24 );      // Disable WARNING MCH
218
219                 if ( ! mchchk_wng_posted )
220                 { 
221                         mchchk_wng_posted = s390_post_warning();
222
223                         if ( mchchk_wng_posted )
224                         {
225                                 up( &s_sem );
226
227                         } /* endif */
228
229                 } /* endif */
230
231         } /* endif */
232 #endif
233
234 #ifdef S390_MACHCHK_DEBUG
235         printk( KERN_INFO "s390_do_machine_check : done \n");
236 #endif
237
238         return;
239 }
240
241 /*
242  * s390_machine_check_handler
243  *
244  * machine check handler, dequeueing machine check entries
245  *  and processing them
246  */
247 static int s390_machine_check_handler( void *parm)
248 {
249         struct semaphore *sem = parm;
250         unsigned long     flags;
251         mache_t          *pmache;
252
253         int               found = 0;
254
255         /* set name to something sensible */
256         strcpy (current->comm, "kmcheck");
257
258
259         /* block all signals */
260         sigfillset(&current->blocked);
261
262 #ifdef S390_MACHCHK_DEBUG
263         printk( KERN_NOTICE "mach_handler : ready\n");
264 #endif  
265
266         do {
267
268 #ifdef S390_MACHCHK_DEBUG
269                 printk( KERN_NOTICE "mach_handler : waiting for wakeup\n");
270 #endif  
271
272                 down_interruptible( sem );
273
274 #ifdef S390_MACHCHK_DEBUG
275                 printk( KERN_NOTICE "\nmach_handler : wakeup ... \n");
276 #endif  
277                 found = 0; /* init ... */
278
279                 __save_flags( flags );
280                 __cli();
281
282                 do {
283
284                 pmache = s390_dequeue_mchchk();
285
286                 if ( pmache )
287                 {
288                         found = 1;
289                 
290                         if ( pmache->mcic.mcc.mcd.cp )
291                         {
292                                 crwe_t *pcrwe_n;
293                                 crwe_t *pcrwe_h;
294
295                                 s390_do_crw_pending( pmache->mc.crwe );
296
297                                 pcrwe_h = pmache->mc.crwe;
298                                 pcrwe_n = pmache->mc.crwe->crwe_next;
299
300                                 pmache->mcic.mcc.mcd.cp = 0;
301                                 pmache->mc.crwe         = NULL;
302
303                                 spin_lock( &crw_queue_lock);
304
305                                 while ( pcrwe_h )
306                                 {
307                                         pcrwe_h->crwe_next = crw_buffer_anchor;
308                                         crw_buffer_anchor  = pcrwe_h;
309                                         pcrwe_h            = pcrwe_n;
310
311                                         if ( pcrwe_h != NULL )
312                                                 pcrwe_n = pcrwe_h->crwe_next;
313
314                                 } /* endwhile */
315
316                                 spin_unlock( &crw_queue_lock);
317
318                         } /* endif */
319
320 #ifdef CONFIG_MACHCHK_WARNING
321                         if ( pmache->mcic.mcc.mcd.w )
322                         {
323                                 ctrl_alt_del();         // shutdown NOW!
324 #ifdef S390_MACHCHK_DEBUG
325                         printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
326 #endif
327                         } /* endif */
328 #endif
329
330                         s390_enqueue_free_mchchk( pmache );
331                 }
332                 else
333                 {
334
335                         // unconditional surrender ...
336 #ifdef S390_MACHCHK_DEBUG
337                         printk( KERN_DEBUG "mach_handler : nothing to do, sleeping\n");
338 #endif  
339
340                 } /* endif */   
341
342                 } while ( pmache );
343
344                 __restore_flags( flags );
345
346         } while ( 1 );
347
348         return( 0);
349 }
350
351 /*
352  * s390_dequeue_mchchk
353  *
354  * Dequeue an entry from the machine check queue
355  *
356  * Note : The queue elements provide for a double linked list.
357  *  We dequeue entries from the tail, and enqueue entries to
358  *  the head.
359  *
360  */
361 static mache_t *s390_dequeue_mchchk( void )
362 {
363         mache_t *qe;
364
365         spin_lock( &mchchk_queue_lock );
366
367         qe = mchchk_queue_tail;
368
369    if ( qe != NULL )
370    {
371       mchchk_queue_tail = qe->prev;
372
373       if ( mchchk_queue_tail != NULL )
374       {
375                         mchchk_queue_tail->next = NULL;
376                 }
377                 else
378       {
379                         mchchk_queue_head = NULL;
380
381       } /* endif */
382
383         } /* endif */
384
385         spin_unlock( &mchchk_queue_lock );
386
387         return qe;
388 }
389
390 /*
391  * s390_enqueue_mchchk
392  *
393  * Enqueue an entry to the machine check queue.
394  *
395  * Note : The queue elements provide for a double linked list.
396  *  We enqueue entries to the head, and dequeue entries from
397  *  the tail.
398  *
399  */
400 static void s390_enqueue_mchchk( mache_t *pmache )
401 {
402         spin_lock( &mchchk_queue_lock );
403
404         if ( pmache != NULL )
405         {
406
407                 if ( mchchk_queue_head == NULL )  /* first element */
408                 {
409                         pmache->next      = NULL;
410                         pmache->prev      = NULL;
411
412                         mchchk_queue_head = pmache;
413                         mchchk_queue_tail = pmache;
414                 }
415                 else /* new head */
416                 {
417                         pmache->prev            = NULL;
418                         pmache->next            = mchchk_queue_head;
419
420                         mchchk_queue_head->prev = pmache;
421                         mchchk_queue_head       = pmache;
422
423                 } /* endif */
424
425         } /* endif */
426
427         spin_unlock( &mchchk_queue_lock );
428
429         return;
430 }
431
432
433 /*
434  * s390_enqueue_free_mchchk
435  *
436  * Enqueue a free entry to the free queue.
437  *
438  * Note : While the queue elements provide for a double linked list,
439  *  the free queue entries are only concatenated by means of a
440  *  single linked list (forward concatenation).
441  *
442  */
443 static void s390_enqueue_free_mchchk( mache_t *pmache )
444 {
445         if ( pmache != NULL)
446         {
447                 memset( pmache, '\0', sizeof( mache_t ));
448
449                 spin_lock( &mchchk_queue_lock );
450                 
451                 pmache->next = mchchk_queue_free;
452
453                 mchchk_queue_free = pmache;
454
455                 spin_unlock( &mchchk_queue_lock );
456
457         } /* endif */
458
459         return;
460 }
461
462 /*
463  * s390_dequeue_free_mchchk
464  *
465  * Dequeue an entry from the free queue.
466  *
467  * Note : While the queue elements provide for a double linked list,
468  *  the free queue entries are only concatenated by means of a
469  *  single linked list (forward concatenation).
470  *
471  */
472 static mache_t *s390_dequeue_free_mchchk( void )
473 {
474         mache_t *qe;
475
476         spin_lock( &mchchk_queue_lock );
477
478         qe = mchchk_queue_free;
479
480         if ( qe != NULL )
481         {
482                 mchchk_queue_free = qe->next;
483
484         } /* endif */
485
486         spin_unlock( &mchchk_queue_lock );
487
488         return qe;
489 }
490
491 /*
492  * s390_collect_crw_info
493  *
494  * Retrieve CRWs. If a CRW was found a machine check element
495  *  is dequeued from the free chain, filled and enqueued to
496  *  be processed.
497  *
498  * The function returns the number of CRWs found.
499  *
500  * Note : We must always be called disabled ...
501  */
502 static int s390_collect_crw_info( void )
503 {
504         crw_t    tcrw;     /* temporarily holds a CRW */
505         int      ccode;    /* condition code from stcrw() */
506         crwe_t  *pcrwe;    /* pointer to CRW buffer entry */
507
508         mache_t *pmache = NULL; /* ptr to mchchk entry */
509         int      chain  = 0;    /* indicate chaining */
510         crwe_t  *pccrw  = NULL; /* ptr to current CRW buffer entry */
511         int      count  = 0;    /* CRW count */
512
513 #ifdef S390_MACHCHK_DEBUG
514         printk( KERN_DEBUG "crw_info : looking for CRWs ...\n");
515 #endif
516
517         do
518         {
519                 ccode = stcrw( (__u32 *)&tcrw);
520
521                 if ( ccode == 0 )
522                 {
523                         count++;
524                         
525 #ifdef S390_MACHCHK_DEBUG
526                         printk( KERN_DEBUG "crw_info : CRW reports "
527                                 "slct=%d, oflw=%d, chn=%d, "
528                                 "rsc=%X, anc=%d, erc=%X, "
529                                 "rsid=%X\n",
530                                 tcrw.slct,
531                                 tcrw.oflw,
532                                 tcrw.chn,
533                                 tcrw.rsc,
534                                 tcrw.anc,
535                                 tcrw.erc,
536                                 tcrw.rsid );
537 #endif
538
539                         /*
540                          * Dequeue a CRW entry from the free chain
541                          *  and process it ...
542                          */
543                         spin_lock( &crw_queue_lock );
544
545                         pcrwe = crw_buffer_anchor;
546
547                         if ( pcrwe == NULL )
548                         {
549                                 spin_unlock( &crw_queue_lock );
550                                 printk( KERN_CRIT"crw_info : "
551                                         "no CRW buffer entries available\n");
552                                 break;
553
554                         } /* endif */
555                         
556                         crw_buffer_anchor = pcrwe->crwe_next;
557                         pcrwe->crwe_next  = NULL;
558
559                         spin_unlock( &crw_queue_lock );
560
561                         memcpy( &(pcrwe->crw), &tcrw, sizeof(crw_t));
562
563                         /*
564                          * If it is the first CRW, chain it to the mchchk
565                          *  buffer entry, otherwise to the last CRW entry.
566                          */
567                         if ( chain == 0 )
568                         {
569                                 pmache = s390_dequeue_free_mchchk();
570
571                                 if ( pmache != NULL )
572                                 {
573                                         memset( pmache, '\0', sizeof(mache_t));
574
575                                         pmache->mcic.mcc.mcd.cp = 1;
576                                         pmache->mc.crwe         = pcrwe;
577                                         pccrw                   = pcrwe;
578
579                                 }
580                                 else
581                                 {
582                                         panic( "crw_info : "
583                                                "unable to dequeue "
584                                                "free mchchk buffer");                           
585
586                                 } /* endif */
587                         }
588                         else
589                         {
590                                 pccrw->crwe_next = pcrwe;
591                                 pccrw            = pcrwe;
592
593                         } /* endif */   
594
595                         if ( pccrw->crw.chn )
596                         {
597 #ifdef S390_MACHCHK_DEBUG
598                                 printk( KERN_DEBUG "crw_info : "
599                                         "chained CRWs pending ...\n\n");
600 #endif
601                                 chain = 1;
602                         }
603                         else
604                         {
605                                 chain = 0;
606
607                                 /*
608                                  * We can enqueue the mchchk buffer if
609                                  *  there aren't more CRWs chained.
610                                  */
611                                 s390_enqueue_mchchk( pmache);
612
613                         } /* endif */
614
615                 } /* endif */
616
617         } while ( ccode == 0 );
618
619         return( count );
620 }
621
622 #ifdef CONFIG_MACHCHK_WARNING
623 /*
624  * s390_post_warning
625  *
626  * Post a warning type machine check
627  *
628  * The function returns 1 when succesfull (panics otherwise)
629  */
630 static int s390_post_warning( void )
631 {
632         mache_t  *pmache = NULL; /* ptr to mchchk entry */
633
634         pmache = s390_dequeue_free_mchchk();
635
636         if ( pmache != NULL )
637         {
638                 memset( pmache, '\0', sizeof(mache_t) );
639
640                 pmache->mcic.mcc.mcd.w = 1;
641
642                 s390_enqueue_mchchk( pmache );
643         }
644         else
645         {
646                 panic(  "post_warning : "
647                         "unable to dequeue "
648                         "free mchchk buffer" );
649         } /* endif */
650
651 #ifdef S390_MACHCHK_DEBUG
652         printk( KERN_DEBUG "post_warning : 1 warning machine check posted\n");
653 #endif
654
655         return ( 1 );
656 }
657 #endif
658