Viewing file: s390mach.c (10.48 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * 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>
#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 );
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;
/* * 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 memory\n");
} /* 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 memory\n");
} /* endif */
#ifdef S390_MACHCHK_DEBUG printk( "init_mach : starting machine check handler\n"); #endif
kernel_thread( s390_machine_check_handler, &s_sem, CLONE_FS | CLONE_FILES);
ctl_clear_bit( 14, 25 ); // disable damage MCH #if 1 ctl_set_bit( 14, 28 ); // enable channel report MCH #endif
#ifdef S390_MACHCHK_DEBUG printk( "init_mach : machine check buffer : head = %08X\n", (unsigned)&mchchk_queue_head); printk( "init_mach : machine check buffer : tail = %08X\n", (unsigned)&mchchk_queue_tail); printk( "init_mach : machine check buffer : free = %08X\n", (unsigned)&mchchk_queue_free); printk( "init_mach : CRW entry buffer anchor = %08X\n", (unsigned)&crw_buffer_anchor); printk( "init_mach : machine check handler ready\n"); #endif
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( "s390_do_machine_check : starting ...\n"); #endif
memcpy( &mcic, &S390_lowcore.mcck_interruption_code, sizeof(__u64)); if ( mcic.mcc.mcd.cp ) // CRW pending ? { crw_count = s390_collect_crw_info();
if ( crw_count ) { up( &s_sem );
} /* endif */
} /* endif */
#ifdef S390_MACHCHK_DEBUG printk( "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( "mach_handler : ready\n"); #endif
do {
#ifdef S390_MACHCHK_DEBUG printk( "mach_handler : waiting for wakeup\n"); #endif
down_interruptible( sem );
#ifdef S390_MACHCHK_DEBUG printk( "\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 */
s390_enqueue_free_mchchk( pmache ); } else {
// unconditional surrender ... #ifdef S390_MACHCHK_DEBUG printk( "mach_handler : nothing to do, sleeping\n"); #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, '\0', 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( "crw_info : looking for CRWs ...\n"); #endif
do { ccode = stcrw( (__u32 *)&tcrw);
if ( ccode == 0 ) { count++; #ifdef S390_MACHCHK_DEBUG printk( "crw_info : CRW reports " "slct=%d, oflw=%d, chn=%d, " "rsc=%X, anc=%d, erc=%X, " "rsid=%X\n", 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 available\n"); 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, '\0', 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( "crw_info : " "chained CRWs pending ...\n\n"); #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 ); }
|