patch-2.4.2 linux/drivers/s390/s390mach.c
Next file: linux/drivers/sbus/audio/amd7930.c
Previous file: linux/drivers/s390/s390io.c
Back to the patch index
Back to the overall index
- Lines: 535
- Date:
Tue Feb 13 14:13:44 2001
- Orig file:
v2.4.1/linux/drivers/s390/s390mach.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.1/linux/drivers/s390/s390mach.c linux/drivers/s390/s390mach.c
@@ -0,0 +1,534 @@
+/*
+ * 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/malloc.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_interuption_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;
+ int 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 );
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)