patch-2.4.4 linux/drivers/s390/s390io.c

Next file: linux/drivers/sbus/char/Makefile
Previous file: linux/drivers/s390/s390dyn.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.3/linux/drivers/s390/s390io.c linux/drivers/s390/s390io.c
@@ -8,8 +8,16 @@
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  *    ChangeLog: 01/04/2001 Holger Smolinski (smolinsk@de.ibm.com)
  *                          Fixed lost interrupts and do_adapter_IO
+ *               xx/xx/xxxx nnn          multiple changes not reflected
+ *               03/12/2001 Ingo Adlung  blacklist= - changed to cio_ignore=  
+ *               03/14/2001 Ingo Adlung  disable interrupts before start_IO
+ *                                        in Path Group processing 
+ *                                       decrease retry2 on busy while 
+ *                                        disabling sync_isc; reset isc_cnt
+ *                                        on io error during sync_isc enablement
  */
 
+#include <linux/module.h>
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
@@ -23,7 +31,9 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
-
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif 
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -34,6 +44,7 @@
 #include <asm/processor.h>
 #include <asm/lowcore.h>
 #include <asm/idals.h>
+#include <asm/uaccess.h> 
 
 #include <asm/s390io.h>
 #include <asm/s390dyn.h>
@@ -47,9 +58,6 @@
 #undef  CONFIG_DEBUG_IO
 #define CONFIG_DEBUG_CRW
 
-#define REIPL_DEVID_MAGIC 0x87654321
-
-struct s390_irqaction  init_IRQ_action;
 unsigned int           highest_subchannel;
 ioinfo_t              *ioinfo_head = NULL;
 ioinfo_t              *ioinfo_tail = NULL;
@@ -58,7 +66,8 @@
 };
 
 static atomic_t    sync_isc     = ATOMIC_INIT(-1);
-                                          // synchronous irq processing lock
+static int         sync_isc_cnt = 0;      // synchronous irq processing lock
+
 static spinlock_t  adapter_lock = SPIN_LOCK_UNLOCKED;
                                           // adapter interrupt lock
 static psw_t      io_sync_wait;           // wait PSW for sync IO, prot. by sync_isc
@@ -70,22 +79,7 @@
 static __u64      irq_IPL_TOD;
 static adapter_int_handler_t adapter_handler   = NULL;
 
-/*
- * Dummy controller type for unused interrupts
- */
-int  do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;}
-int  enable_none(unsigned int irq) { return(-ENODEV); }
-int  disable_none(unsigned int irq) { return(-ENODEV); }
-
-struct hw_interrupt_type no_irq_type = {
-	"none",
-	do_none,
-	enable_none,
-	disable_none
-};
-
 static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs);
-static int  s390_setup_irq(unsigned int irq, struct s390_irqaction * new);
 static void s390_process_subchannels( void);
 static void s390_device_recognition_all( void);
 static void s390_device_recognition_irq( int irq);
@@ -94,8 +88,11 @@
 static int  s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid);
 static int  s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid);
 static int  s390_process_IRQ( unsigned int irq );
+static int  enable_subchannel( unsigned int irq);
 static int  disable_subchannel( unsigned int irq);
 
+void chan_proc_init( void );
+
 static inline void do_adapter_IO( __u32 intparm );
 
 int  s390_DevicePathVerification( int irq, __u8 domask );
@@ -108,6 +105,305 @@
 
 asmlinkage void do_IRQ( struct pt_regs regs );
 
+
+/* 
+ * "Blacklisting" of certain devices:
+ * Device numbers given in the commandline as blacklist=... won't be known to Linux
+ * These can be single devices or ranges of devices
+ *
+ * Introduced by Cornelia Huck <cohuck@de.ibm.com>
+ * Most of it shamelessly taken from dasd.c 
+ */
+
+typedef struct dev_blacklist_range_t {
+     unsigned int from;                   /* beginning of range */
+     unsigned int to;                     /* end of range */
+     struct dev_blacklist_range_t *next;  /* next range in list */
+} dev_blacklist_range_t;
+
+static dev_blacklist_range_t *dev_blacklist_range_head = NULL; /* Anchor for list of ranges */
+static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
+
+/* Handling of the blacklist ranges */
+
+/* 
+ * Function: blacklist_range_create
+ * Creates a range from the given parameters
+ */
+static inline dev_blacklist_range_t *blacklist_range_create( int from, int to)
+{
+     dev_blacklist_range_t *range = NULL;
+     range = ( dev_blacklist_range_t *) alloc_bootmem( sizeof( dev_blacklist_range_t ) );
+     if (range == NULL)
+	  return NULL;
+     memset( range, 0, sizeof( dev_blacklist_range_t ));
+     range->from = from;
+     if (to == 0) {           /* only a single device is given */
+	  range->to = from;
+     } else {
+	  range->to = to;
+     }
+     return range;
+}
+
+/* 
+ * Function: blacklist_range_destroy
+ * Free the given range
+ */
+
+static inline void blacklist_range_destroy( dev_blacklist_range_t *range )
+{
+     kfree( range );
+}
+
+/*
+ * Function: blacklist_range_append
+ * Append a range to the list of blacklisted ranges anchored at dev_blacklist_range_head
+ */
+
+static inline void blacklist_range_append( dev_blacklist_range_t *range )
+{
+     dev_blacklist_range_t *temp;
+     long flags;
+
+     spin_lock_irqsave( &blacklist_lock, flags );
+     if (dev_blacklist_range_head == NULL) {
+	  dev_blacklist_range_head = range;
+     } else {
+	  for ( temp = dev_blacklist_range_head;
+		temp && temp->next;
+		temp = temp->next );
+	  temp->next = range;
+     }
+     spin_unlock_irqrestore( &blacklist_lock, flags );
+}
+
+/*
+ * Function: blacklist_range_dechain
+ * Remove a range from the list of blacklisted ranges
+ */
+
+static inline void blacklist_range_dechain( dev_blacklist_range_t *range )
+{
+     dev_blacklist_range_t *temp, *prev = NULL;
+     long flags;
+     
+     spin_lock_irqsave( &blacklist_lock, flags );
+     for ( temp = dev_blacklist_range_head; temp != NULL; temp = temp->next ) {
+	  if ( temp == range )
+	       break;
+	  prev = temp;
+     }
+     if (!temp)
+	  BUG();
+     if (prev) {
+	  prev->next = range->next;
+     } else {
+	  dev_blacklist_range_head = range->next;
+     }
+     spin_unlock_irqrestore( &blacklist_lock, flags );
+}
+
+/* 
+ * Function: blacklist_range_add
+ * Creates a range from the specified arguments and appends it to the list of
+ * blacklisted devices 
+ */
+
+static inline dev_blacklist_range_t *blacklist_range_add( int from, int to )
+{
+     dev_blacklist_range_t *temp;
+
+     temp = blacklist_range_create( from, to );
+     if (!temp) 
+	  return NULL;
+     blacklist_range_append( temp );
+     return temp;
+}
+
+/* 
+ * Function: blacklist_range_remove
+ * Removes a range from the blacklist chain
+ */
+
+static inline void blacklist_range_remove( int from, int to )
+{
+     dev_blacklist_range_t *temp;
+
+     for ( temp = dev_blacklist_range_head; 
+	   (temp->from != from) && (temp->to != to);
+	   temp = temp->next );
+     blacklist_range_dechain( temp );
+     blacklist_range_destroy( temp );
+}
+
+/* Parsing the commandline for blacklist parameters */
+
+/* 
+ * Variable to hold the blacklisted devices given by the parameter line
+ * blacklist=...
+ */
+char *blacklist[256] = {NULL, };
+
+/*
+ * Get the blacklist=... items from the parameter line
+ */
+
+static void blacklist_split_parm_string (char *str)
+{
+	char *tmp = str;
+	int count = 0;
+	do {
+		char *end;
+		int len;
+		end = strchr (tmp, ',');
+		if (end == NULL) {
+			len = strlen (tmp) + 1;
+		} else {
+			len = (long) end - (long) tmp + 1;
+			*end = '\0';
+			end++;
+		}
+		blacklist[count] = alloc_bootmem (len * sizeof (char) );
+		if (blacklist == NULL) {
+			printk (KERN_WARNING "can't store blacklist= parameter no %d\n", count + 1);
+			break;
+		}
+		memset (blacklist[count], 0, len * sizeof (char));
+		memcpy (blacklist[count], tmp, len * sizeof (char));
+		count++;
+		tmp = end;
+	} while (tmp != NULL && *tmp != '\0');
+}
+
+/*
+ * The blacklist parameters as one concatenated string
+ */
+
+static char blacklist_parm_string[1024] __initdata = {0,};
+
+
+/* 
+ * function: blacklist_strtoul
+ * Strip leading '0x' and interpret the values as Hex
+ */
+static inline int blacklist_strtoul (char *str, char **stra)
+{
+	char *temp = str;
+	int val;
+	if (*temp == '0') {
+		temp++;		                /* strip leading zero */
+		if (*temp == 'x')
+			temp++;	                /* strip leading x */
+	}
+	val = simple_strtoul (temp, &temp, 16);	/* interpret anything as hex */
+	*stra = temp;
+	return val;
+}
+
+/*
+ * Function: blacklist_parse
+ * Parse the parameters given to blacklist=... 
+ * Add the blacklisted devices to the blacklist chain
+ */
+
+static inline void blacklist_parse( char **str )
+{
+     char *temp;
+     int from, to;
+
+     while (*str) {
+	  temp = *str;
+	  from = 0;
+	  to = 0;
+	  
+	  from = blacklist_strtoul( temp, &temp );
+	  if (*temp == '-') {
+	       temp++;
+	       to = blacklist_strtoul( temp, &temp );
+	  }
+	  if (!blacklist_range_add( from, to )) {
+	       printk( KERN_WARNING "Blacklisting range from %X to %X failed!\n", from, to);
+	  }
+#ifdef CONFIG_DEBUG_IO
+	  printk( "Blacklisted range from %X to %X\n", from, to );
+#endif
+	  str++;
+     }
+}
+
+
+/*
+ * Initialisation of blacklist 
+ */
+
+void __init blacklist_init( void )
+{
+#ifdef CONFIG_DEBUG_IO     
+     printk( "Reading blacklist...\n");
+#endif
+     blacklist_split_parm_string( blacklist_parm_string );
+     blacklist_parse( blacklist );
+}
+
+
+/*
+ * Get all the blacklist parameters from parameter line
+ */
+
+void __init blacklist_setup (char *str, int *ints)
+{
+	int len = strlen (blacklist_parm_string);
+	if (len != 0) {
+		strcat (blacklist_parm_string, ",");
+	}
+	strcat (blacklist_parm_string, str);
+}
+
+int __init blacklist_call_setup (char *str)
+{
+        int dummy;
+#ifdef CONFIG_DEBUG_IO
+	printk( "Reading blacklist parameters...\n" );
+#endif
+        blacklist_setup(str,&dummy);
+	
+	blacklist_init(); /* Blacklist ranges must be ready when device recognition starts */
+       
+	return 1;
+}
+
+__setup ("cio_ignore=", blacklist_call_setup);
+
+/* Checking if devices are blacklisted */
+
+/*
+ * Function: is_blacklisted
+ * Returns 1 if the given devicenumber can be found in the blacklist, otherwise 0.
+ */
+
+static inline int is_blacklisted( int devno )
+{
+     dev_blacklist_range_t *temp;
+
+     if (dev_blacklist_range_head == NULL) {  
+	  /* no blacklist */
+	  return 0;
+     }
+
+     temp = dev_blacklist_range_head;
+     while (temp) {
+	  if ((temp->from <= devno) && (temp->to >= devno)) {
+	       return 1;                      /* Deviceno is blacklisted */
+	  }
+	  temp = temp->next;
+     }
+     return 0;
+}
+
+/* End of blacklist handling */
+
+
 void s390_displayhex(char *str,void *ptr,s32 cnt);
 
 void s390_displayhex(char *str,void *ptr,s32 cnt)
@@ -129,7 +425,7 @@
 	}
 }
 
-static int __init cio_setup( char *parm, int *ints)
+static int __init cio_setup( char *parm )
 {
 	if ( !strcmp( parm, "yes") )
 	{
@@ -219,8 +515,8 @@
                               const char              *devname,
                               void                    *dev_id)
 {
-	int               retval;
-	struct s390_irqaction *action;
+	int		retval = 0;
+	unsigned long	flags;
 
 	if (irq >= __MAX_SUBCHANNELS)
 		return -EINVAL;
@@ -228,52 +524,42 @@
 	if ( !io_handler || !dev_id )
 		return -EINVAL;
 
+	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+		return -ENODEV;
+
+
 	/*
-	 * during init_IRQ() processing we don't have memory
-	 *  management yet, thus need to use a statically
-	 *  allocated irqaction control block
+	 * The following block of code has to be executed atomically
 	 */
-	if ( init_IRQ_complete )
+	s390irq_spin_lock_irqsave( irq, flags);
+
+	if ( !ioinfo[irq]->ui.flags.ready )
 	{
-		action = (struct s390_irqaction *)
-		            kmalloc( sizeof(struct s390_irqaction),
-		                     GFP_KERNEL);
+		ioinfo[irq]->irq_desc.handler = io_handler;
+		ioinfo[irq]->irq_desc.name    = devname;
+		ioinfo[irq]->irq_desc.dev_id  = dev_id;
+		ioinfo[irq]->ui.flags.ready   = 1;
+
+		enable_subchannel(irq);
 	}
 	else
 	{
-		action = &init_IRQ_action;
-
-	} /* endif */
-
-	if (!action)
-	{
-		return -ENOMEM;
+		/*
+		 *  interrupt already owned, and shared interrupts
+		 *   aren't supported on S/390.
+		 */
+		retval = -EBUSY;
 
 	} /* endif */
 
-	action->handler = io_handler;
-	action->flags   = irqflags;
-	action->name    = devname;
-	action->dev_id  = dev_id;
-
-	retval = s390_setup_irq( irq, action);
-
-	if ( init_IRQ_complete )
-	{
-		if ( !retval )
-		{
-			s390_DevicePathVerification( irq, 0 );
-		}
-		else
-		{
-			kfree(action);
+	s390irq_spin_unlock_irqrestore(irq,flags);
 
-		} /* endif */
 
-	} /* endif */
 
 	if ( retval == 0 )
 	{
+		s390_DevicePathVerification( irq, 0 );
+
 		ioinfo[irq]->ui.flags.newreq = 1;
 		ioinfo[irq]->nopfunc         = not_oper_handler;  	
 	}
@@ -333,10 +619,9 @@
 	 * disable the device and reset all IRQ info if
 	 *  the IRQ is actually owned by the handler ...
 	 */
-	if ( ioinfo[irq]->irq_desc.action )
+	if ( ioinfo[irq]->ui.flags.ready )
 	{
-		if (    (dev_id == ioinfo[irq]->irq_desc.action->dev_id  )
-		     || (dev_id == (devstat_t *)REIPL_DEVID_MAGIC) )
+		if ( dev_id == ioinfo[irq]->irq_desc.dev_id  )
 		{
 			/* start deregister */
 			ioinfo[irq]->ui.flags.unready = 1;
@@ -413,14 +698,8 @@
 		
 			} while ( ret == -EBUSY );
 
-			if ( init_IRQ_complete )
-				kfree( ioinfo[irq]->irq_desc.action );
-
-			ioinfo[irq]->irq_desc.action           = NULL;
-			ioinfo[irq]->ui.flags.ready            = 0;
-			ioinfo[irq]->irq_desc.handler->enable  = enable_none;
-			ioinfo[irq]->irq_desc.handler->disable = disable_none;
-			ioinfo[irq]->ui.flags.unready          = 0; /* deregister ended */
+			ioinfo[irq]->ui.flags.ready   = 0;
+			ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */
 
 			ioinfo[irq]->nopfunc = NULL;
 
@@ -458,14 +737,11 @@
 	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 		return( -ENODEV);
 
-	s390irq_spin_lock_irqsave(irq, flags);
+	if ( !ioinfo[irq]->ui.flags.ready )
+		return -ENODEV;
 
-	/*
-	 * At this point we may actually have a pending interrupt being active
-	 * on another CPU. So don't touch the IRQ_INPROGRESS bit..
-	 */
-	ioinfo[irq]->irq_desc.status |= IRQ_DISABLED;
-	ret = ioinfo[irq]->irq_desc.handler->disable(irq);
+	s390irq_spin_lock_irqsave(irq, flags);
+	ret = disable_subchannel(irq);
 	s390irq_spin_unlock_irqrestore(irq, flags);
 
 	synchronize_irq();
@@ -481,11 +757,11 @@
 	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 		return( -ENODEV);
 
-	s390irq_spin_lock_irqsave(irq, flags);
-
-	ioinfo[irq]->irq_desc.status = 0;
-	ret = ioinfo[irq]->irq_desc.handler->enable(irq);
+	if ( !ioinfo[irq]->ui.flags.ready )
+		return -ENODEV;
 
+	s390irq_spin_lock_irqsave(irq, flags);
+	ret = enable_subchannel(irq);
 	s390irq_spin_unlock_irqrestore(irq, flags);
 
 	return(ret);
@@ -717,48 +993,6 @@
 }
 
 
-int s390_setup_irq( unsigned int irq, struct s390_irqaction * new)
-{
-	unsigned long      flags;
-	int                rc = 0;
-
-	if ( ioinfo[irq] == INVALID_STORAGE_AREA )
-	{
-		return( -ENODEV);
-   }
-
-	/*
-	 * The following block of code has to be executed atomically
-	 */
-	s390irq_spin_lock_irqsave( irq, flags);
-
-	if ( ioinfo[irq]->irq_desc.action == NULL )
-	{
-		ioinfo[irq]->irq_desc.action           = new;
-		ioinfo[irq]->irq_desc.status           = 0;
-		ioinfo[irq]->irq_desc.handler->enable  = enable_subchannel;
-		ioinfo[irq]->irq_desc.handler->disable = disable_subchannel;
-		ioinfo[irq]->irq_desc.handler->handle  = handle_IRQ_event;
-
-		ioinfo[irq]->ui.flags.ready            = 1;
-
-		ioinfo[irq]->irq_desc.handler->enable(irq);
-	}
-	else
-	{
-		/*
-		 *  interrupt already owned, and shared interrupts
-		 *   aren't supported on S/390.
-		 */
-		rc = -EBUSY;
-
-	} /* endif */
-
-	s390irq_spin_unlock_irqrestore(irq,flags);
-
-	return( rc);
-}
-
 void s390_init_IRQ( void )
 {
 	unsigned long flags;     /* PSW flags */
@@ -776,7 +1010,8 @@
 
 	p_init_schib = alloc_bootmem_low( sizeof(schib_t));
 	p_init_irb   = alloc_bootmem_low( sizeof(irb_t));
-
+	
+	
 	/*
 	 * As we don't know about the calling environment
 	 *  we assure running disabled. Before leaving the
@@ -844,7 +1079,7 @@
 	/*
 	 * setup ORB
 	 */
-  	ioinfo[irq]->orb.intparm = (__u32)(__u64)&ioinfo[irq]->u_intparm;
+  	ioinfo[irq]->orb.intparm = (__u32)(long)&ioinfo[irq]->u_intparm;
 	ioinfo[irq]->orb.fmt     = 1;
 
 	ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH);
@@ -864,13 +1099,10 @@
 
 #ifdef CONFIG_ARCH_S390X
 	/* 
-	 * for 64 bit we always support 64 bit IDAWs with 2k page
-	 *  size only
-	 * FIXTHEM: OSA microcode currently has problems with 4k
-         *          we would like to use 4k.
+	 * for 64 bit we always support 64 bit IDAWs with 4k page size only
 	 */
 	ioinfo[irq]->orb.c64 = 1;
-	ioinfo[irq]->orb.i2k = 1;
+	ioinfo[irq]->orb.i2k = 0;
 #endif
 
 	ioinfo[irq]->orb.cpa = (__u32)virt_to_phys( cpa);
@@ -912,7 +1144,7 @@
 			 *
 			 * Note : donīt clear saved irb info in case of sense !
 			 */
-			memset( &((devstat_t *)ioinfo[irq]->irq_desc.action->dev_id)->ii.irb,
+			memset( &((devstat_t *)ioinfo[irq]->irq_desc.dev_id)->ii.irb,
 				'\0', sizeof( irb_t) );
 		} /* endif */
 
@@ -1086,7 +1318,7 @@
 		/*
 		 * initialize the device driver specific devstat irb area
 		 */
-		memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb,
+		memset( &((devstat_t *) ioinfo[irq]->irq_desc.dev_id)->ii.irb,
 		        '\0', sizeof( irb_t) );
 
 		/*
@@ -1178,8 +1410,8 @@
 				         ioinfo[irq]->devstat.devno );
 
 				s390_displayhex( buffer,
-		((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data,
-		((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt);
+					ioinfo[irq]->irq_desc.dev_id->ii.sense.data,
+					ioinfo[irq]->irq_desc.dev_id->rescnt);
 
 			} /* endif */
 			}
@@ -1221,7 +1453,7 @@
 
 		ret = -ENODEV;
 
-		memcpy( ioinfo[irq]->irq_desc.action->dev_id,
+		memcpy( ioinfo[irq]->irq_desc.dev_id,
 		        &(ioinfo[irq]->devstat),
 		        sizeof( devstat_t) );
 
@@ -1579,7 +1811,7 @@
 			/*
 			 * initialize the device driver specific devstat irb area
 			 */
-			memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb,
+			memset( &ioinfo[irq]->irq_desc.dev_id->ii.irb,
 			        '\0', sizeof( irb_t) );
 
 			/*
@@ -1840,7 +2072,7 @@
 			/*
 			 * initialize the device driver specific devstat irb area
 			 */
-			memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb,
+			memset( &ioinfo[irq]->irq_desc.dev_id->ii.irb,
 			        '\0', sizeof( irb_t) );
 
 			/*
@@ -2007,7 +2239,6 @@
 	unsigned int           fctl;       /* function control */
 	unsigned int           stctl;      /* status   control */
 	unsigned int           actl;       /* activity control */
-	struct s390_irqaction *action;
 	struct pt_regs         regs;       /* for interface compatibility only */
 
 	int               issense         = 0;
@@ -2015,6 +2246,7 @@
 	int               allow4handler   = 1;
 	int               chnchk          = 0;
 	devstat_t        *dp;
+	devstat_t        *udp;
 #if 0
 	int               cpu             = smp_processor_id();
 
@@ -2029,8 +2261,8 @@
 
 	} /* endif */
 
-	action = ioinfo[irq]->irq_desc.action;
-	dp     = &ioinfo[irq]->devstat;
+	dp  = &ioinfo[irq]->devstat;
+	udp = ioinfo[irq]->irq_desc.dev_id;
 	
 
 #ifdef CONFIG_DEBUG_IO
@@ -2040,7 +2272,7 @@
 	 *  available when the device possibly becomes ready again. In
 	 *  this case we perform delayed disable_subchannel() processing.
 	 */
-	if ( action == NULL )
+	if ( !ioinfo[irq]->ui.flags.ready )
 	{
 		if ( !ioinfo[irq]->ui.flags.d_disable )
 		{
@@ -2151,7 +2383,11 @@
 
 	} /* endif */
 
-	if (    (dp->ii.irb.scsw.stctl == SCSW_STCTL_STATUS_PEND)
+	if( dp->ii.irb.scsw.ectl==0)
+	{
+		issense=0;
+	}
+	else if (    (dp->ii.irb.scsw.stctl == SCSW_STCTL_STATUS_PEND)
 	     &&	(dp->ii.irb.scsw.eswf  == 0                     ))
 	{
 		issense = 0;
@@ -2262,7 +2498,7 @@
 		/*
 		 * take fast exit if no handler is available
 		 */
-		if ( !action )
+		if ( !ioinfo[irq]->ui.flags.ready )
 			return( ending_status );     		
 
 		/*
@@ -2291,9 +2527,7 @@
 				 *  sensing. When finally calling the IRQ handler we must not overlay
 				 *  the original device status but copy the sense data only.
 				 */
-				memcpy( action->dev_id,
-				        dp,
-				        sizeof( devstat_t) );
+				memcpy( udp, dp, sizeof( devstat_t) );
 
 				s_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
 				s_ccw->cda      = (__u32)virt_to_phys( ioinfo[irq]->sense_data );
@@ -2405,14 +2639,14 @@
                  "BASIC SENSE bytes avail %d\n",
                  irq, sense_count );
 #endif
-				ioinfo[irq]->ui.flags.w4sense          = 0;
-				((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
-				((devstat_t *)(action->dev_id))->scnt  = sense_count;
+				ioinfo[irq]->ui.flags.w4sense = 0;
+				udp->flag |= DEVSTAT_FLAG_SENSE_AVAIL;
+				udp->scnt  = sense_count;
 
 				if ( sense_count >= 0 )
 				{
-					memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data,
-					        &(ioinfo[irq]->sense_data),
+					memcpy( udp->ii.sense.data,
+					        ioinfo[irq]->sense_data,
 					        sense_count);
 				}
 				else
@@ -2430,7 +2664,7 @@
 			}
 			else
 			{
-				memcpy( action->dev_id, dp, sdevstat );
+				memcpy( udp, dp, sdevstat );
 
 			}  /* endif */
 
@@ -2456,8 +2690,8 @@
 				ioinfo[irq]->ui.flags.repall   = 0;
 				ioinfo[irq]->ui.flags.w4final  = 0;
 
-				dp->flag             |= DEVSTAT_FINAL_STATUS;
-				action->dev_id->flag |= DEVSTAT_FINAL_STATUS;
+				dp->flag  |= DEVSTAT_FINAL_STATUS;
+				udp->flag |= DEVSTAT_FINAL_STATUS;
 
 			} /* endif */
 
@@ -2486,10 +2720,10 @@
 				ioinfo[irq]->ui.flags.repall   = 0;
 				ioinfo[irq]->ui.flags.w4final  = 0;
 
-				dp->flag             |= DEVSTAT_FINAL_STATUS;
-				action->dev_id->flag |= DEVSTAT_FINAL_STATUS;
+				dp->flag  |= DEVSTAT_FINAL_STATUS;
+				udp->flag |= DEVSTAT_FINAL_STATUS;
 
-				action->handler( irq, action->dev_id, &regs );
+				ioinfo[irq]->irq_desc.handler( irq, udp, &regs );
 
 				//
 				// reset intparm after final status or we will badly present unsolicited
@@ -2521,7 +2755,7 @@
 					 */
 					if ( ret )
 					{
-						action->handler( irq, action->dev_id, &regs );
+						ioinfo[irq]->irq_desc.handler( irq, udp, &regs );
 
 					} /* endif */
 
@@ -2539,17 +2773,17 @@
 				 */
 				if ( dp->cstat & SCHN_STAT_PCI )
 				{
-					action->dev_id->flag |= DEVSTAT_PCI;
-					dp->cstat            &= ~SCHN_STAT_PCI;
+					udp->flag |= DEVSTAT_PCI;
+					dp->cstat &= ~SCHN_STAT_PCI;
 				}
 
 				if ( actl & SCSW_ACTL_SUSPENDED )
 				{
-					action->dev_id->flag |= DEVSTAT_SUSPENDED;
+					udp->flag |= DEVSTAT_SUSPENDED;
 
 				} /* endif */
 
-				action->handler( irq, action->dev_id, &regs );
+				ioinfo[irq]->irq_desc.handler( irq, udp, &regs );
 
 			} /* endif */
 
@@ -2623,16 +2857,16 @@
 		/*
 		 * take fast exit if no handler is available
 		 */
-		if ( !action )
+		if ( !ioinfo[irq]->ui.flags.ready )
 			return( ending_status );     		
 
-		memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat );
+		memcpy( udp, &(ioinfo[irq]->devstat), sdevstat );
 
 		ioinfo[irq]->devstat.intparm  = 0;
 
 		if ( !ioinfo[irq]->ui.flags.s_pend )
 		{
-			action->handler( irq, action->dev_id, &regs );
+			ioinfo[irq]->irq_desc.handler( irq, udp, &regs );
 
 		} /* endif */
 
@@ -2856,9 +3090,9 @@
 		if ( atomic_read( &sync_isc ) != irq )
 			atomic_compare_and_swap_spin( -1, irq, &sync_isc );
 
-		ioinfo[irq]->syncnt++;
+		sync_isc_cnt++;
 		
-		if ( ioinfo[irq]->syncnt > 255 ) /* fixme : magic number */
+		if ( sync_isc_cnt > 255 ) /* fixme : magic number */
 		{
 			panic("Too many recursive calls to enable_sync_isc");
 
@@ -2866,7 +3100,7 @@
 		/*
 		 * we only run the STSCH/MSCH path for the first enablement
 		 */
-		else if ( ioinfo[irq]->syncnt == 1)
+		else if ( sync_isc_cnt == 1 )
 		{
 			ioinfo[irq]->ui.flags.syncio = 1;
 
@@ -2931,9 +3165,16 @@
 			} /* endif */
 
 		} /* endif */
+
+		if ( rc )	// can only happen if stsch/msch fails
+			sync_isc_cnt = 0;
 	}
 	else
 	{
+#ifdef CONFIG_SYNC_ISC_PARANOIA
+		panic( "enable_sync_isc: called with invalid %x\n", irq );
+#endif
+
 		rc = -EINVAL;
 
 	} /* endif */
@@ -2960,7 +3201,14 @@
 		 *  msch() processing we may face another pending
 		 *  status we have to process recursively (sync).
 		 */
-		if ( (ioinfo[irq]->syncnt-1) == 0 )
+
+#ifdef CONFIG_SYNC_ISC_PARANOIA
+		if ( atomic_read( &sync_isc ) != irq )
+			panic( "disable_sync_isc: called for %x while %x locked\n",
+				irq, atomic_read( &sync_isc ) );
+#endif
+
+		if ( sync_isc_cnt == 1 )
 		{
 			ccode = stsch( irq, &(ioinfo[irq]->schib) );
 
@@ -2989,6 +3237,8 @@
 						retry2--;
 						break;
 					case 2:
+					        retry2--;
+						udelay( 100); // give it time
 						break;
 					default:
 						retry2 = 0;
@@ -3011,20 +3261,26 @@
 
 			} while ( retry1 && ccode );
 
-			ioinfo[irq]->syncnt          = 0;
 			ioinfo[irq]->ui.flags.syncio = 0;
-			
+		
+			sync_isc_cnt = 0;	
 			atomic_set( &sync_isc, -1);
 
 		}
 		else
 		{
-			ioinfo[irq]->syncnt--;
+			sync_isc_cnt--;
 
-   	} /* endif */
+		} /* endif */
 	}
 	else
 	{
+#ifdef CONFIG_SYNC_ISC_PARANOIA
+		if ( atomic_read( &sync_isc ) != -1 )
+			panic( "disable_sync_isc: called with invalid %x while %x locked\n", 
+				irq, atomic_read( &sync_isc ) );
+#endif
+
 		rc = -EINVAL;
 
 	} /* endif */
@@ -3446,7 +3702,7 @@
 				rdc_ccw->flags    = CCW_FLAG_SLI;
 				set_normalized_cda( rdc_ccw, (unsigned long)rdc_buf );
 
-				memset( (devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id),
+				memset( ioinfo[irq]->irq_desc.dev_id,
 				        '\0',
 				        sizeof( devstat_t));
 
@@ -3457,7 +3713,7 @@
 				                     DOIO_WAIT_FOR_INTERRUPT
 				                      | DOIO_DONT_CALL_INTHDLR );
 				retry--;
-				devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag;   
+				devflag = ioinfo[irq]->irq_desc.dev_id->flag;   
     
 				clear_normalized_cda( rdc_ccw);  
 
@@ -3579,7 +3835,7 @@
 		}
 		else
 		{
-			pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
+			pdevstat = ioinfo[irq]->irq_desc.dev_id;
 
 		} /* endif */
 
@@ -3998,7 +4254,6 @@
 void s390_device_recognition_irq( int irq )
 {
 	int           ret;
-	unsigned long psw_flags;
 
 	/*
 	 * We issue the SenseID command on I/O subchannels we think are
@@ -4194,7 +4449,17 @@
 
 		if ( p_schib->pmcw.dnv )
 		{
-			if ( ioinfo[irq] == INVALID_STORAGE_AREA )
+		     if ( is_blacklisted( p_schib->pmcw.dev )) {
+			  /* 
+			   * This device must not be known to Linux. So we simply say that 
+			   * there is no device and return ENODEV.
+			   */
+#ifdef CONFIG_DEBUG_IO
+			  printk( "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
+#endif
+			  ret = -ENODEV;
+		     } else {
+		        if ( ioinfo[irq] == INVALID_STORAGE_AREA )
 			{	
 				if ( !init_IRQ_complete )
 				{
@@ -4213,8 +4478,6 @@
 				memcpy( &ioinfo[irq]->schib,
 			           p_init_schib,
 			           sizeof( schib_t));
-				ioinfo[irq]->irq_desc.status  = IRQ_DISABLED;
-				ioinfo[irq]->irq_desc.handler = &no_irq_type;
 			
 				/*
 				 * We have to insert the new ioinfo element
@@ -4438,6 +4701,7 @@
 				ret                        = -ENODEV;    	
 
 			} /* endif */
+		     }
 		}
 		else
 		{
@@ -4520,7 +4784,7 @@
 	{
 		inlreq   = 0;
 		irq_ret  = 0;
-		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
+		pdevstat = ioinfo[irq]->irq_desc.dev_id;
 
   	} /* endif */
 
@@ -5045,6 +5309,8 @@
 	ccw1_t    *spid_ccw;    /* ccw area for SPID command */
 	devstat_t  devstat;     /* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
+        unsigned long flags;
+
 
 	int        irq_ret = 0; /* return code */
 	int        retry   = 5; /* retry count */
@@ -5087,13 +5353,13 @@
    }
 	else
 	{
-		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
+		pdevstat = ioinfo[irq]->irq_desc.dev_id;
 
 	} /* endif */
 
 	if ( irq_ret == 0 )
 	{
-		s390irq_spin_lock( irq);
+		s390irq_spin_lock_irqsave( irq, flags);
 
 		if ( init_IRQ_complete )
 		{
@@ -5233,7 +5499,7 @@
 
 		} /* endif */
 
-		s390irq_spin_unlock( irq);
+		s390irq_spin_unlock_irqrestore( irq, flags);
 
 		/*
 		 * If we installed the irq action handler we have to
@@ -5301,7 +5567,7 @@
    }
 	else
 	{
-		pdevstat = ioinfo[irq]->irq_desc.action->dev_id;
+		pdevstat = ioinfo[irq]->irq_desc.dev_id;
 
 	} /* endif */
 
@@ -5547,7 +5813,7 @@
 					     && ( nopfunc != NULL ) )
 					{
 						
-						free_irq( irq,ioinfo[irq]->irq_desc.action->dev_id );
+						free_irq( irq,ioinfo[irq]->irq_desc.dev_id );
 						nopfunc( irq,DEVSTAT_DEVICE_GONE );			
 
 					} /* endif */
@@ -5659,10 +5925,170 @@
 reipl ( int sch )
 {
 	int i;
+	s390_dev_info_t dev_info;
 
-	for ( i = 0; i < highest_subchannel; i ++ ) {
-	    free_irq ( i, (void*)REIPL_DEVID_MAGIC );
+	for ( i = 0; i <= highest_subchannel; i ++ ) 
+	{
+		if (    get_dev_info_by_irq( i, &dev_info ) == 0
+                    && (dev_info.status & DEVSTAT_DEVICE_OWNED) )
+		{
+			free_irq ( i, ioinfo[i]->irq_desc.dev_id );
+		}
 	}
+
 	do_reipl( 0x10000 | sch );
 }
 
+/* Display info on subchannels in /proc/subchannels. *
+ * Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.      */
+
+typedef struct {
+     char *data;
+     int len;
+} tempinfo_t;
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+
+static struct proc_dir_entry *chan_subch_entry;
+
+static int chan_subch_open( struct inode *inode, struct file *file)
+{
+     int rc = 0;
+     int size = 1;
+     int len = 0;
+     int i = 0;
+     int j = 0;
+     tempinfo_t *info;
+     
+     info = (tempinfo_t *) vmalloc( sizeof (tempinfo_t));
+     if (info == NULL) {
+	  printk( KERN_WARNING "No memory available for data\n");
+	  return -ENOMEM;
+     } else {
+	  file->private_data = ( void * ) info;
+     }
+
+     size += (highest_subchannel+1) * 128;
+     info->data = (char *) vmalloc( size );
+     
+     if (size && info->data == NULL) {
+		printk (KERN_WARNING "No memory available for data\n");
+		vfree (info);
+		return -ENOMEM;
+	}
+     
+     len += sprintf( info->data+len, 
+		     "Device sch.  Dev Type/Model CU  in use  PIM PAM POM LPUM CHPIDs\n");
+     len += sprintf( info->data+len, 
+		     "--------------------------------------------------------------------------\n");
+
+     for ( i=0; i <= highest_subchannel; i++) {
+	  if ( !((ioinfo[i] == NULL) || (ioinfo[i] == INVALID_STORAGE_AREA) || !(ioinfo[i]->ui.flags.oper)) ) {
+	       len += sprintf( info->data+len, 
+			       "%04X   %04X  ", 
+			       ioinfo[i]->schib.pmcw.dev, 
+			       i );
+	       if ( ioinfo[i]->senseid.dev_type != 0 ) {
+		    len += sprintf( info->data+len, 
+				    "%04X/%02X   %04X/%02X", 
+				    ioinfo[i]->senseid.dev_type,
+				    ioinfo[i]->senseid.dev_model, 
+				    ioinfo[i]->senseid.cu_type,
+				    ioinfo[i]->senseid.cu_model );
+	       } else {
+		    len += sprintf( info->data+len, 
+				    "%04X/%02X          ", 
+				    ioinfo[i]->senseid.cu_type, 
+				    ioinfo[i]->senseid.cu_model );
+	       }
+	       if (ioinfo[i]->ui.flags.ready) {
+		    len += sprintf( info->data+len, "  yes " );
+	       } else {
+		    len += sprintf( info->data+len, "      " );
+	       }
+	       len += sprintf( info->data+len,
+			       "    %02X  %02X  %02X  %02X   ",
+			       ioinfo[i]->schib.pmcw.pim,
+			       ioinfo[i]->schib.pmcw.pam,
+			       ioinfo[i]->schib.pmcw.pom,
+			       ioinfo[i]->schib.pmcw.lpum );
+	       for ( j=0; j < 8; j++ ) {
+		    len += sprintf( info->data+len,
+				    "%02X",
+				    ioinfo[i]->schib.pmcw.chpid[j] );
+		    if (j==3) {
+			 len += sprintf( info->data+len, " " );
+		    }
+	       }
+	       len += sprintf( info->data+len, "\n" );
+	  }
+     }
+     info->len = len;
+
+     return rc;
+}
+
+static int chan_subch_close( struct inode *inode, struct file *file)
+{
+     int rc = 0;
+     tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+
+     if (p_info) {
+	  if (p_info->data)
+	       vfree( p_info->data );
+	  vfree( p_info );
+     }
+     
+     return rc;
+}
+
+static ssize_t chan_subch_read( struct file *file, char *user_buf, size_t user_len, loff_t * offset)
+{
+     loff_t len;
+     tempinfo_t *p_info = (tempinfo_t *) file->private_data;
+     
+     if ( *offset>=p_info->len) {
+	  return 0;
+     } else {
+	  len = MIN(user_len, (p_info->len - *offset));
+	  if (copy_to_user( user_buf, &(p_info->data[*offset]), len))
+	       return -EFAULT; 
+	  (* offset) += len;
+	  return len;
+     }
+}
+
+static struct file_operations chan_subch_file_ops =
+{
+     read:chan_subch_read,
+     open:chan_subch_open,
+     release:chan_subch_close,
+};
+
+void chan_proc_init( void )
+{
+     chan_subch_entry = create_proc_entry( "subchannels", S_IFREG|S_IRUGO, &proc_root);
+     chan_subch_entry->proc_fops = &chan_subch_file_ops;
+}
+
+__initcall(chan_proc_init);
+
+void chan_proc_cleanup( void )
+{
+     remove_proc_entry( "subchannels", &proc_root);
+}
+
+EXPORT_SYMBOL(halt_IO);
+EXPORT_SYMBOL(clear_IO);
+EXPORT_SYMBOL(do_IO);
+EXPORT_SYMBOL(resume_IO);
+EXPORT_SYMBOL(ioinfo);
+EXPORT_SYMBOL(get_dev_info_by_irq);
+EXPORT_SYMBOL(get_dev_info_by_devno);
+EXPORT_SYMBOL(get_irq_by_devno);
+EXPORT_SYMBOL(get_devno_by_irq);
+EXPORT_SYMBOL(get_irq_first);
+EXPORT_SYMBOL(get_irq_next);
+EXPORT_SYMBOL(read_conf_data);
+EXPORT_SYMBOL(read_dev_chars);
+EXPORT_SYMBOL(s390_request_irq_special);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)