s390mach.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:14k
- /*
- * arch/s390/kernel/s390mach.c
- * S/390 machine check handler,
- * currently only channel-reports are supported
- *
- * S390 version
- * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Ingo Adlung (adlung@de.ibm.com)
- */
- #include <linux/config.h>
- #include <linux/spinlock.h>
- #include <linux/init.h>
- #include <linux/slab.h>
- #ifdef CONFIG_SMP
- #include <linux/smp.h>
- #endif
- #include <asm/irq.h>
- #include <asm/lowcore.h>
- #include <asm/semaphore.h>
- #include <asm/s390io.h>
- #include <asm/s390dyn.h>
- #include <asm/s390mach.h>
- #ifdef CONFIG_MACHCHK_WARNING
- #include <asm/signal.h>
- #endif
- extern void ctrl_alt_del(void);
- #define S390_MACHCHK_DEBUG
- static int s390_machine_check_handler( void * parm );
- static void s390_enqueue_mchchk( mache_t *mchchk );
- static mache_t *s390_dequeue_mchchk( void );
- static void s390_enqueue_free_mchchk( mache_t *mchchk );
- static mache_t *s390_dequeue_free_mchchk( void );
- static int s390_collect_crw_info( void );
- #ifdef CONFIG_MACHCHK_WARNING
- static int s390_post_warning( void );
- #endif
- static mache_t *mchchk_queue_head = NULL;
- static mache_t *mchchk_queue_tail = NULL;
- static mache_t *mchchk_queue_free = NULL;
- static crwe_t *crw_buffer_anchor = NULL;
- static spinlock_t mchchk_queue_lock = SPIN_LOCK_UNLOCKED;
- static spinlock_t crw_queue_lock = SPIN_LOCK_UNLOCKED;
- static struct semaphore s_sem;
- #ifdef CONFIG_MACHCHK_WARNING
- static int mchchk_wng_posted = 0;
- #endif
- /*
- * s390_init_machine_check
- *
- * initialize machine check handling
- */
- void s390_init_machine_check( void )
- {
- crwe_t *pcrwe; /* CRW buffer element pointer */
- mache_t *pmache; /* machine check element pointer */
- init_MUTEX_LOCKED( &s_sem );
- pcrwe = kmalloc( MAX_CRW_PENDING * sizeof( crwe_t), GFP_KERNEL);
- if ( pcrwe )
- {
- int i;
- crw_buffer_anchor = pcrwe;
- for ( i=0; i < MAX_CRW_PENDING-1; i++)
- {
- pcrwe->crwe_next = (crwe_t *)((unsigned long)pcrwe + sizeof(crwe_t));
- pcrwe = pcrwe->crwe_next;
- } /* endfor */
- pcrwe->crwe_next = NULL;
- }
- else
- {
- panic( "s390_init_machine_check : unable to obtain memoryn");
- } /* endif */
- pmache = kmalloc( MAX_MACH_PENDING * sizeof( mache_t), GFP_KERNEL);
- if ( pmache )
- {
- int i;
- for ( i=0; i < MAX_MACH_PENDING; i++)
- {
- s390_enqueue_free_mchchk( pmache );
- pmache = (mache_t *)((unsigned long)pmache + sizeof(mache_t));
- } /* endfor */
- }
- else
- {
- panic( "s390_init_machine_check : unable to obtain memoryn");
- } /* endif */
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_NOTICE "init_mach : starting machine check handlern");
- #endif
- kernel_thread( s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES);
- ctl_clear_bit( 14, 25 ); // disable damage MCH
- ctl_set_bit( 14, 26 ); /* enable degradation MCH */
- ctl_set_bit( 14, 27 ); /* enable system recovery MCH */
- #if 1
- ctl_set_bit( 14, 28 ); // enable channel report MCH
- #endif
- #ifdef CONFIG_MACHCK_WARNING
- ctl_set_bit( 14, 24); /* enable warning MCH */
- #endif
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "init_mach : machine check buffer : head = %08Xn",
- (unsigned)&mchchk_queue_head);
- printk( KERN_DEBUG "init_mach : machine check buffer : tail = %08Xn",
- (unsigned)&mchchk_queue_tail);
- printk( KERN_DEBUG "init_mach : machine check buffer : free = %08Xn",
- (unsigned)&mchchk_queue_free);
- printk( KERN_DEBUG "init_mach : CRW entry buffer anchor = %08Xn",
- (unsigned)&crw_buffer_anchor);
- printk( KERN_DEBUG "init_mach : machine check handler readyn");
- #endif
- return;
- }
- static void s390_handle_damage(char * msg){
- unsigned long caller = (unsigned long) __builtin_return_address(0);
- printk(KERN_EMERG "%sn", msg);
- #ifdef CONFIG_SMP
- smp_send_stop();
- #endif
- disabled_wait(caller);
- return;
- }
- /*
- * s390_do_machine_check
- *
- * mchine check pre-processor, collecting the machine check info,
- * queueing it and posting the machine check handler for processing.
- */
- void s390_do_machine_check( void )
- {
- int crw_count;
- mcic_t mcic;
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_INFO "s390_do_machine_check : starting ...n");
- #endif
- memcpy( &mcic,
- &S390_lowcore.mcck_interruption_code,
- sizeof(__u64));
-
- if (mcic.mcc.mcd.sd) /* system damage */
- s390_handle_damage("received system damage machine checkn");
- if (mcic.mcc.mcd.pd) /* instruction processing damage */
- s390_handle_damage("received instruction processing damage machine checkn");
- if (mcic.mcc.mcd.se) /* storage error uncorrected */
- s390_handle_damage("received storage error uncorrected machine checkn");
- if (mcic.mcc.mcd.sc) /* storage error corrected */
- printk(KERN_WARNING "received storage error corrected machine checkn");
- if (mcic.mcc.mcd.ke) /* storage key-error uncorrected */
- s390_handle_damage("received storage key-error uncorrected machine checkn");
- if (mcic.mcc.mcd.ds && mcic.mcc.mcd.fa) /* storage degradation */
- s390_handle_damage("received storage degradation machine checkn");
- if ( mcic.mcc.mcd.cp ) // CRW pending ?
- {
- crw_count = s390_collect_crw_info();
- if ( crw_count )
- {
- up( &s_sem );
- } /* endif */
- } /* endif */
- #ifdef CONFIG_MACHCHK_WARNING
- /*
- * The warning may remain for a prolonged period on the bare iron.
- * (actually till the machine is powered off, or until the problem is gone)
- * So we just stop listening for the WARNING MCH and prevent continuously
- * being interrupted. One caveat is however, that we must do this per
- * processor and cannot use the smp version of ctl_clear_bit().
- * On VM we only get one interrupt per virtally presented machinecheck.
- * Though one suffices, we may get one interrupt per (virtual) processor.
- */
- if ( mcic.mcc.mcd.w ) // WARNING pending ?
- {
- // Use single machine clear, as we cannot handle smp right now
- __ctl_clear_bit( 14, 24 ); // Disable WARNING MCH
- if ( ! mchchk_wng_posted )
- {
- mchchk_wng_posted = s390_post_warning();
- if ( mchchk_wng_posted )
- {
- up( &s_sem );
- } /* endif */
- } /* endif */
- } /* endif */
- #endif
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_INFO "s390_do_machine_check : done n");
- #endif
- return;
- }
- /*
- * s390_machine_check_handler
- *
- * machine check handler, dequeueing machine check entries
- * and processing them
- */
- static int s390_machine_check_handler( void *parm)
- {
- struct semaphore *sem = parm;
- unsigned long flags;
- mache_t *pmache;
- int found = 0;
- /* set name to something sensible */
- strcpy (current->comm, "kmcheck");
- /* block all signals */
- sigfillset(¤t->blocked);
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_NOTICE "mach_handler : readyn");
- #endif
- do {
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_NOTICE "mach_handler : waiting for wakeupn");
- #endif
- down_interruptible( sem );
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_NOTICE "nmach_handler : wakeup ... n");
- #endif
- found = 0; /* init ... */
- __save_flags( flags );
- __cli();
- do {
- pmache = s390_dequeue_mchchk();
- if ( pmache )
- {
- found = 1;
-
- if ( pmache->mcic.mcc.mcd.cp )
- {
- crwe_t *pcrwe_n;
- crwe_t *pcrwe_h;
- s390_do_crw_pending( pmache->mc.crwe );
- pcrwe_h = pmache->mc.crwe;
- pcrwe_n = pmache->mc.crwe->crwe_next;
- pmache->mcic.mcc.mcd.cp = 0;
- pmache->mc.crwe = NULL;
- spin_lock( &crw_queue_lock);
- while ( pcrwe_h )
- {
- pcrwe_h->crwe_next = crw_buffer_anchor;
- crw_buffer_anchor = pcrwe_h;
- pcrwe_h = pcrwe_n;
- if ( pcrwe_h != NULL )
- pcrwe_n = pcrwe_h->crwe_next;
- } /* endwhile */
- spin_unlock( &crw_queue_lock);
- } /* endif */
- #ifdef CONFIG_MACHCHK_WARNING
- if ( pmache->mcic.mcc.mcd.w )
- {
- ctrl_alt_del(); // shutdown NOW!
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "mach_handler : kill -SIGPWR initn");
- #endif
- } /* endif */
- #endif
- s390_enqueue_free_mchchk( pmache );
- }
- else
- {
- // unconditional surrender ...
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "mach_handler : nothing to do, sleepingn");
- #endif
- } /* endif */
- } while ( pmache );
- __restore_flags( flags );
- } while ( 1 );
- return( 0);
- }
- /*
- * s390_dequeue_mchchk
- *
- * Dequeue an entry from the machine check queue
- *
- * Note : The queue elements provide for a double linked list.
- * We dequeue entries from the tail, and enqueue entries to
- * the head.
- *
- */
- static mache_t *s390_dequeue_mchchk( void )
- {
- mache_t *qe;
- spin_lock( &mchchk_queue_lock );
- qe = mchchk_queue_tail;
- if ( qe != NULL )
- {
- mchchk_queue_tail = qe->prev;
- if ( mchchk_queue_tail != NULL )
- {
- mchchk_queue_tail->next = NULL;
- }
- else
- {
- mchchk_queue_head = NULL;
- } /* endif */
- } /* endif */
- spin_unlock( &mchchk_queue_lock );
- return qe;
- }
- /*
- * s390_enqueue_mchchk
- *
- * Enqueue an entry to the machine check queue.
- *
- * Note : The queue elements provide for a double linked list.
- * We enqueue entries to the head, and dequeue entries from
- * the tail.
- *
- */
- static void s390_enqueue_mchchk( mache_t *pmache )
- {
- spin_lock( &mchchk_queue_lock );
- if ( pmache != NULL )
- {
- if ( mchchk_queue_head == NULL ) /* first element */
- {
- pmache->next = NULL;
- pmache->prev = NULL;
- mchchk_queue_head = pmache;
- mchchk_queue_tail = pmache;
- }
- else /* new head */
- {
- pmache->prev = NULL;
- pmache->next = mchchk_queue_head;
- mchchk_queue_head->prev = pmache;
- mchchk_queue_head = pmache;
- } /* endif */
- } /* endif */
- spin_unlock( &mchchk_queue_lock );
- return;
- }
- /*
- * s390_enqueue_free_mchchk
- *
- * Enqueue a free entry to the free queue.
- *
- * Note : While the queue elements provide for a double linked list,
- * the free queue entries are only concatenated by means of a
- * single linked list (forward concatenation).
- *
- */
- static void s390_enqueue_free_mchchk( mache_t *pmache )
- {
- if ( pmache != NULL)
- {
- memset( pmache, ' ', sizeof( mache_t ));
- spin_lock( &mchchk_queue_lock );
-
- pmache->next = mchchk_queue_free;
- mchchk_queue_free = pmache;
- spin_unlock( &mchchk_queue_lock );
- } /* endif */
- return;
- }
- /*
- * s390_dequeue_free_mchchk
- *
- * Dequeue an entry from the free queue.
- *
- * Note : While the queue elements provide for a double linked list,
- * the free queue entries are only concatenated by means of a
- * single linked list (forward concatenation).
- *
- */
- static mache_t *s390_dequeue_free_mchchk( void )
- {
- mache_t *qe;
- spin_lock( &mchchk_queue_lock );
- qe = mchchk_queue_free;
- if ( qe != NULL )
- {
- mchchk_queue_free = qe->next;
- } /* endif */
- spin_unlock( &mchchk_queue_lock );
- return qe;
- }
- /*
- * s390_collect_crw_info
- *
- * Retrieve CRWs. If a CRW was found a machine check element
- * is dequeued from the free chain, filled and enqueued to
- * be processed.
- *
- * The function returns the number of CRWs found.
- *
- * Note : We must always be called disabled ...
- */
- static int s390_collect_crw_info( void )
- {
- crw_t tcrw; /* temporarily holds a CRW */
- int ccode; /* condition code from stcrw() */
- crwe_t *pcrwe; /* pointer to CRW buffer entry */
- mache_t *pmache = NULL; /* ptr to mchchk entry */
- int chain = 0; /* indicate chaining */
- crwe_t *pccrw = NULL; /* ptr to current CRW buffer entry */
- int count = 0; /* CRW count */
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "crw_info : looking for CRWs ...n");
- #endif
- do
- {
- ccode = stcrw( (__u32 *)&tcrw);
- if ( ccode == 0 )
- {
- count++;
-
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "crw_info : CRW reports "
- "slct=%d, oflw=%d, chn=%d, "
- "rsc=%X, anc=%d, erc=%X, "
- "rsid=%Xn",
- tcrw.slct,
- tcrw.oflw,
- tcrw.chn,
- tcrw.rsc,
- tcrw.anc,
- tcrw.erc,
- tcrw.rsid );
- #endif
- /*
- * Dequeue a CRW entry from the free chain
- * and process it ...
- */
- spin_lock( &crw_queue_lock );
- pcrwe = crw_buffer_anchor;
- if ( pcrwe == NULL )
- {
- spin_unlock( &crw_queue_lock );
- printk( KERN_CRIT"crw_info : "
- "no CRW buffer entries availablen");
- break;
- } /* endif */
-
- crw_buffer_anchor = pcrwe->crwe_next;
- pcrwe->crwe_next = NULL;
- spin_unlock( &crw_queue_lock );
- memcpy( &(pcrwe->crw), &tcrw, sizeof(crw_t));
- /*
- * If it is the first CRW, chain it to the mchchk
- * buffer entry, otherwise to the last CRW entry.
- */
- if ( chain == 0 )
- {
- pmache = s390_dequeue_free_mchchk();
- if ( pmache != NULL )
- {
- memset( pmache, ' ', sizeof(mache_t));
- pmache->mcic.mcc.mcd.cp = 1;
- pmache->mc.crwe = pcrwe;
- pccrw = pcrwe;
- }
- else
- {
- panic( "crw_info : "
- "unable to dequeue "
- "free mchchk buffer");
- } /* endif */
- }
- else
- {
- pccrw->crwe_next = pcrwe;
- pccrw = pcrwe;
- } /* endif */
- if ( pccrw->crw.chn )
- {
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "crw_info : "
- "chained CRWs pending ...nn");
- #endif
- chain = 1;
- }
- else
- {
- chain = 0;
- /*
- * We can enqueue the mchchk buffer if
- * there aren't more CRWs chained.
- */
- s390_enqueue_mchchk( pmache);
- } /* endif */
- } /* endif */
- } while ( ccode == 0 );
- return( count );
- }
- #ifdef CONFIG_MACHCHK_WARNING
- /*
- * s390_post_warning
- *
- * Post a warning type machine check
- *
- * The function returns 1 when succesfull (panics otherwise)
- */
- static int s390_post_warning( void )
- {
- mache_t *pmache = NULL; /* ptr to mchchk entry */
- pmache = s390_dequeue_free_mchchk();
- if ( pmache != NULL )
- {
- memset( pmache, ' ', sizeof(mache_t) );
- pmache->mcic.mcc.mcd.w = 1;
- s390_enqueue_mchchk( pmache );
- }
- else
- {
- panic( "post_warning : "
- "unable to dequeue "
- "free mchchk buffer" );
- } /* endif */
- #ifdef S390_MACHCHK_DEBUG
- printk( KERN_DEBUG "post_warning : 1 warning machine check postedn");
- #endif
- return ( 1 );
- }
- #endif