OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [s390/] [s390mach.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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_MACHCHK_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
 * s390_do_machine_check
157
 *
158
 * mchine check pre-processor, collecting the machine check info,
159
 *  queueing it and posting the machine check handler for processing.
160
 */
161
void s390_do_machine_check( void )
162
{
163
        int      crw_count;
164
        mcic_t   mcic;
165
 
166
#ifdef S390_MACHCHK_DEBUG
167
        printk( KERN_INFO "s390_do_machine_check : starting ...\n");
168
#endif
169
 
170
        memcpy( &mcic,
171
                &S390_lowcore.mcck_interruption_code,
172
                sizeof(__u64));
173
 
174
        if (mcic.mcc.mcd.sd) /* system damage */
175
                s390_handle_damage("received system damage machine check\n");
176
 
177
        if (mcic.mcc.mcd.pd) /* instruction processing damage */
178
                s390_handle_damage("received instruction processing damage machine check\n");
179
 
180
        if (mcic.mcc.mcd.se) /* storage error uncorrected */
181
                s390_handle_damage("received storage error uncorrected machine check\n");
182
 
183
        if (mcic.mcc.mcd.sc) /* storage error corrected */
184
                printk(KERN_WARNING "received storage error corrected machine check\n");
185
 
186
        if (mcic.mcc.mcd.ke) /* storage key-error uncorrected */
187
                s390_handle_damage("received storage key-error uncorrected machine check\n");
188
 
189
        if (mcic.mcc.mcd.ds && mcic.mcc.mcd.fa) /* storage degradation */
190
                s390_handle_damage("received storage degradation machine check\n");
191
 
192
        if ( mcic.mcc.mcd.cp )  // CRW pending ?
193
        {
194
                crw_count = s390_collect_crw_info();
195
 
196
                if ( crw_count )
197
                {
198
                        up( &s_sem );
199
 
200
                } /* endif */
201
 
202
        } /* endif */
203
#ifdef CONFIG_MACHCHK_WARNING
204
/*
205
 * The warning may remain for a prolonged period on the bare iron.
206
 * (actually till the machine is powered off, or until the problem is gone)
207
 * So we just stop listening for the WARNING MCH and prevent continuously
208
 * being interrupted.  One caveat is however, that we must do this per
209
 * processor and cannot use the smp version of ctl_clear_bit().
210
 * On VM we only get one interrupt per virtally presented machinecheck.
211
 * Though one suffices, we may get one interrupt per (virtual) processor.
212
 */
213
        if ( mcic.mcc.mcd.w )   // WARNING pending ?
214
        {
215
                // Use single machine clear, as we cannot handle smp right now
216
                __ctl_clear_bit( 14, 24 );      // Disable WARNING MCH
217
 
218
                if ( ! mchchk_wng_posted )
219
                {
220
                        mchchk_wng_posted = s390_post_warning();
221
 
222
                        if ( mchchk_wng_posted )
223
                        {
224
                                up( &s_sem );
225
 
226
                        } /* endif */
227
 
228
                } /* endif */
229
 
230
        } /* endif */
231
#endif
232
 
233
#ifdef S390_MACHCHK_DEBUG
234
        printk( KERN_INFO "s390_do_machine_check : done \n");
235
#endif
236
 
237
        return;
238
}
239
 
240
/*
241
 * s390_machine_check_handler
242
 *
243
 * machine check handler, dequeueing machine check entries
244
 *  and processing them
245
 */
246
static int s390_machine_check_handler( void *parm)
247
{
248
        struct semaphore *sem = parm;
249
        unsigned long     flags;
250
        mache_t          *pmache;
251
 
252
        int               found = 0;
253
 
254
        /* set name to something sensible */
255
        strcpy (current->comm, "kmcheck");
256
 
257
 
258
        /* block all signals */
259
        sigfillset(&current->blocked);
260
 
261
#ifdef S390_MACHCHK_DEBUG
262
        printk( KERN_NOTICE "mach_handler : ready\n");
263
#endif  
264
 
265
        do {
266
 
267
#ifdef S390_MACHCHK_DEBUG
268
                printk( KERN_NOTICE "mach_handler : waiting for wakeup\n");
269
#endif  
270
 
271
                down_interruptible( sem );
272
 
273
#ifdef S390_MACHCHK_DEBUG
274
                printk( KERN_NOTICE "\nmach_handler : wakeup ... \n");
275
#endif  
276
                found = 0; /* init ... */
277
 
278
                __save_flags( flags );
279
                __cli();
280
 
281
                do {
282
 
283
                pmache = s390_dequeue_mchchk();
284
 
285
                if ( pmache )
286
                {
287
                        found = 1;
288
 
289
                        if ( pmache->mcic.mcc.mcd.cp )
290
                        {
291
                                crwe_t *pcrwe_n;
292
                                crwe_t *pcrwe_h;
293
 
294
                                s390_do_crw_pending( pmache->mc.crwe );
295
 
296
                                pcrwe_h = pmache->mc.crwe;
297
                                pcrwe_n = pmache->mc.crwe->crwe_next;
298
 
299
                                pmache->mcic.mcc.mcd.cp = 0;
300
                                pmache->mc.crwe         = NULL;
301
 
302
                                spin_lock( &crw_queue_lock);
303
 
304
                                while ( pcrwe_h )
305
                                {
306
                                        pcrwe_h->crwe_next = crw_buffer_anchor;
307
                                        crw_buffer_anchor  = pcrwe_h;
308
                                        pcrwe_h            = pcrwe_n;
309
 
310
                                        if ( pcrwe_h != NULL )
311
                                                pcrwe_n = pcrwe_h->crwe_next;
312
 
313
                                } /* endwhile */
314
 
315
                                spin_unlock( &crw_queue_lock);
316
 
317
                        } /* endif */
318
 
319
#ifdef CONFIG_MACHCHK_WARNING
320
                        if ( pmache->mcic.mcc.mcd.w )
321
                        {
322
                                ctrl_alt_del();         // shutdown NOW!
323
#ifdef S390_MACHCHK_DEBUG
324
                        printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
325
#endif
326
                        } /* endif */
327
#endif
328
 
329
#ifdef CONFIG_MACHCHK_WARNING
330
                        if ( pmache->mcic.mcc.mcd.w )
331
                        {
332
                                ctrl_alt_del();         // shutdown NOW!
333
#ifdef S390_MACHCHK_DEBUG
334
                        printk( KERN_DEBUG "mach_handler : kill -SIGPWR init\n");
335
#endif
336
                        } /* endif */
337
#endif
338
 
339
                        s390_enqueue_free_mchchk( pmache );
340
                }
341
                else
342
                {
343
 
344
                        // unconditional surrender ...
345
#ifdef S390_MACHCHK_DEBUG
346
                        printk( KERN_DEBUG "mach_handler : nothing to do, sleeping\n");
347
#endif  
348
 
349
                } /* endif */
350
 
351
                } while ( pmache );
352
 
353
                __restore_flags( flags );
354
 
355
        } while ( 1 );
356
 
357
        return( 0);
358
}
359
 
360
/*
361
 * s390_dequeue_mchchk
362
 *
363
 * Dequeue an entry from the machine check queue
364
 *
365
 * Note : The queue elements provide for a double linked list.
366
 *  We dequeue entries from the tail, and enqueue entries to
367
 *  the head.
368
 *
369
 */
370
static mache_t *s390_dequeue_mchchk( void )
371
{
372
        mache_t *qe;
373
 
374
        spin_lock( &mchchk_queue_lock );
375
 
376
        qe = mchchk_queue_tail;
377
 
378
   if ( qe != NULL )
379
   {
380
      mchchk_queue_tail = qe->prev;
381
 
382
      if ( mchchk_queue_tail != NULL )
383
      {
384
                        mchchk_queue_tail->next = NULL;
385
                }
386
                else
387
      {
388
                        mchchk_queue_head = NULL;
389
 
390
      } /* endif */
391
 
392
        } /* endif */
393
 
394
        spin_unlock( &mchchk_queue_lock );
395
 
396
        return qe;
397
}
398
 
399
/*
400
 * s390_enqueue_mchchk
401
 *
402
 * Enqueue an entry to the machine check queue.
403
 *
404
 * Note : The queue elements provide for a double linked list.
405
 *  We enqueue entries to the head, and dequeue entries from
406
 *  the tail.
407
 *
408
 */
409
static void s390_enqueue_mchchk( mache_t *pmache )
410
{
411
        spin_lock( &mchchk_queue_lock );
412
 
413
        if ( pmache != NULL )
414
        {
415
 
416
                if ( mchchk_queue_head == NULL )  /* first element */
417
                {
418
                        pmache->next      = NULL;
419
                        pmache->prev      = NULL;
420
 
421
                        mchchk_queue_head = pmache;
422
                        mchchk_queue_tail = pmache;
423
                }
424
                else /* new head */
425
                {
426
                        pmache->prev            = NULL;
427
                        pmache->next            = mchchk_queue_head;
428
 
429
                        mchchk_queue_head->prev = pmache;
430
                        mchchk_queue_head       = pmache;
431
 
432
                } /* endif */
433
 
434
        } /* endif */
435
 
436
        spin_unlock( &mchchk_queue_lock );
437
 
438
        return;
439
}
440
 
441
 
442
/*
443
 * s390_enqueue_free_mchchk
444
 *
445
 * Enqueue a free entry to the free queue.
446
 *
447
 * Note : While the queue elements provide for a double linked list,
448
 *  the free queue entries are only concatenated by means of a
449
 *  single linked list (forward concatenation).
450
 *
451
 */
452
static void s390_enqueue_free_mchchk( mache_t *pmache )
453
{
454
        if ( pmache != NULL)
455
        {
456
                memset( pmache, '\0', sizeof( mache_t ));
457
 
458
                spin_lock( &mchchk_queue_lock );
459
 
460
                pmache->next = mchchk_queue_free;
461
 
462
                mchchk_queue_free = pmache;
463
 
464
                spin_unlock( &mchchk_queue_lock );
465
 
466
        } /* endif */
467
 
468
        return;
469
}
470
 
471
/*
472
 * s390_dequeue_free_mchchk
473
 *
474
 * Dequeue an entry from the free queue.
475
 *
476
 * Note : While the queue elements provide for a double linked list,
477
 *  the free queue entries are only concatenated by means of a
478
 *  single linked list (forward concatenation).
479
 *
480
 */
481
static mache_t *s390_dequeue_free_mchchk( void )
482
{
483
        mache_t *qe;
484
 
485
        spin_lock( &mchchk_queue_lock );
486
 
487
        qe = mchchk_queue_free;
488
 
489
        if ( qe != NULL )
490
        {
491
                mchchk_queue_free = qe->next;
492
 
493
        } /* endif */
494
 
495
        spin_unlock( &mchchk_queue_lock );
496
 
497
        return qe;
498
}
499
 
500
/*
501
 * s390_collect_crw_info
502
 *
503
 * Retrieve CRWs. If a CRW was found a machine check element
504
 *  is dequeued from the free chain, filled and enqueued to
505
 *  be processed.
506
 *
507
 * The function returns the number of CRWs found.
508
 *
509
 * Note : We must always be called disabled ...
510
 */
511
static int s390_collect_crw_info( void )
512
{
513
        crw_t    tcrw;     /* temporarily holds a CRW */
514
        int      ccode;    /* condition code from stcrw() */
515
        crwe_t  *pcrwe;    /* pointer to CRW buffer entry */
516
 
517
        mache_t *pmache = NULL; /* ptr to mchchk entry */
518
        int      chain  = 0;    /* indicate chaining */
519
        crwe_t  *pccrw  = NULL; /* ptr to current CRW buffer entry */
520
        int      count  = 0;    /* CRW count */
521
 
522
#ifdef S390_MACHCHK_DEBUG
523
        printk( KERN_DEBUG "crw_info : looking for CRWs ...\n");
524
#endif
525
 
526
        do
527
        {
528
                ccode = stcrw( (__u32 *)&tcrw);
529
 
530
                if ( ccode == 0 )
531
                {
532
                        count++;
533
 
534
#ifdef S390_MACHCHK_DEBUG
535
                        printk( KERN_DEBUG "crw_info : CRW reports "
536
                                "slct=%d, oflw=%d, chn=%d, "
537
                                "rsc=%X, anc=%d, erc=%X, "
538
                                "rsid=%X\n",
539
                                tcrw.slct,
540
                                tcrw.oflw,
541
                                tcrw.chn,
542
                                tcrw.rsc,
543
                                tcrw.anc,
544
                                tcrw.erc,
545
                                tcrw.rsid );
546
#endif
547
 
548
                        /*
549
                         * Dequeue a CRW entry from the free chain
550
                         *  and process it ...
551
                         */
552
                        spin_lock( &crw_queue_lock );
553
 
554
                        pcrwe = crw_buffer_anchor;
555
 
556
                        if ( pcrwe == NULL )
557
                        {
558
                                spin_unlock( &crw_queue_lock );
559
                                printk( KERN_CRIT"crw_info : "
560
                                        "no CRW buffer entries available\n");
561
                                break;
562
 
563
                        } /* endif */
564
 
565
                        crw_buffer_anchor = pcrwe->crwe_next;
566
                        pcrwe->crwe_next  = NULL;
567
 
568
                        spin_unlock( &crw_queue_lock );
569
 
570
                        memcpy( &(pcrwe->crw), &tcrw, sizeof(crw_t));
571
 
572
                        /*
573
                         * If it is the first CRW, chain it to the mchchk
574
                         *  buffer entry, otherwise to the last CRW entry.
575
                         */
576
                        if ( chain == 0 )
577
                        {
578
                                pmache = s390_dequeue_free_mchchk();
579
 
580
                                if ( pmache != NULL )
581
                                {
582
                                        memset( pmache, '\0', sizeof(mache_t));
583
 
584
                                        pmache->mcic.mcc.mcd.cp = 1;
585
                                        pmache->mc.crwe         = pcrwe;
586
                                        pccrw                   = pcrwe;
587
 
588
                                }
589
                                else
590
                                {
591
                                        panic( "crw_info : "
592
                                               "unable to dequeue "
593
                                               "free mchchk buffer");
594
 
595
                                } /* endif */
596
                        }
597
                        else
598
                        {
599
                                pccrw->crwe_next = pcrwe;
600
                                pccrw            = pcrwe;
601
 
602
                        } /* endif */
603
 
604
                        if ( pccrw->crw.chn )
605
                        {
606
#ifdef S390_MACHCHK_DEBUG
607
                                printk( KERN_DEBUG "crw_info : "
608
                                        "chained CRWs pending ...\n\n");
609
#endif
610
                                chain = 1;
611
                        }
612
                        else
613
                        {
614
                                chain = 0;
615
 
616
                                /*
617
                                 * We can enqueue the mchchk buffer if
618
                                 *  there aren't more CRWs chained.
619
                                 */
620
                                s390_enqueue_mchchk( pmache);
621
 
622
                        } /* endif */
623
 
624
                } /* endif */
625
 
626
        } while ( ccode == 0 );
627
 
628
        return( count );
629
}
630
 
631
#ifdef CONFIG_MACHCHK_WARNING
632
/*
633
 * s390_post_warning
634
 *
635
 * Post a warning type machine check
636
 *
637
 * The function returns 1 when succesfull (panics otherwise)
638
 */
639
static int s390_post_warning( void )
640
{
641
        mache_t  *pmache = NULL; /* ptr to mchchk entry */
642
 
643
        pmache = s390_dequeue_free_mchchk();
644
 
645
        if ( pmache != NULL )
646
        {
647
                memset( pmache, '\0', sizeof(mache_t) );
648
 
649
                pmache->mcic.mcc.mcd.w = 1;
650
 
651
                s390_enqueue_mchchk( pmache );
652
        }
653
        else
654
        {
655
                panic(  "post_warning : "
656
                        "unable to dequeue "
657
                        "free mchchk buffer" );
658
        } /* endif */
659
 
660
#ifdef S390_MACHCHK_DEBUG
661
        printk( KERN_DEBUG "post_warning : 1 warning machine check posted\n");
662
#endif
663
 
664
        return ( 1 );
665
}
666
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.