[PATCH] s390 (4/6): common i/o layer.

From: Martin Schwidefsky (schwidefsky@de.ibm.com)
Date: Thu Jul 17 2003 - 11:38:58 EST


 - Fix two memory leaks.
 - Clear pending status in cio_enable_subchannel.
 - Don't call device_unregister from interrupt context.
 - Fix refcounting problems on static device structures for the ccw console.
 - Delete timeouts for qdio after successful startup.

diffstat:
 drivers/s390/cio/chsc.c | 10 +++---
 drivers/s390/cio/cio.c | 7 +++-
 drivers/s390/cio/device.c | 66 ++++++++++++++++++++++++++++++------------
 drivers/s390/cio/device.h | 2 +
 drivers/s390/cio/device_fsm.c | 12 +++++--
 drivers/s390/cio/qdio.c | 48 +++++++++---------------------
 6 files changed, 85 insertions(+), 60 deletions(-)

diff -urN linux-2.6.0-test1/drivers/s390/cio/chsc.c linux-2.6.0-s390/drivers/s390/cio/chsc.c
--- linux-2.6.0-test1/drivers/s390/cio/chsc.c Mon Jul 14 05:34:40 2003
+++ linux-2.6.0-s390/drivers/s390/cio/chsc.c Thu Jul 17 17:27:33 2003
@@ -1,7 +1,7 @@
 /*
  * drivers/s390/cio/chsc.c
  * S/390 common I/O routines -- channel subsystem call
- * $Revision: 1.73 $
+ * $Revision: 1.74 $
  *
  * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  * IBM Corporation
@@ -206,6 +206,7 @@
         if (!page)
                 return -ENOMEM;
 
+ err = 0;
         for (irq = 0; irq <= highest_subchannel; irq++) {
                 /*
                  * retrieve information for each sch
@@ -222,13 +223,14 @@
                                        "not work\n", err);
                                 cio_chsc_err_msg = 1;
                         }
- return err;
+ goto out;
                 }
                 clear_page(page);
         }
         cio_chsc_desc_avail = 1;
+out:
         free_page((unsigned long)page);
- return 0;
+ return err;
 }
 
 __initcall(chsc_get_sch_descriptions);
@@ -428,7 +430,7 @@
                         ret = css_probe_device(irq);
                         if (ret == -ENXIO)
                                 /* We're through */
- return;
+ break;
                         continue;
                 }
         
diff -urN linux-2.6.0-test1/drivers/s390/cio/cio.c linux-2.6.0-s390/drivers/s390/cio/cio.c
--- linux-2.6.0-test1/drivers/s390/cio/cio.c Thu Jul 17 17:27:30 2003
+++ linux-2.6.0-s390/drivers/s390/cio/cio.c Thu Jul 17 17:27:33 2003
@@ -1,7 +1,7 @@
 /*
  * drivers/s390/cio/cio.c
  * S/390 common I/O routines -- low level i/o calls
- * $Revision: 1.98 $
+ * $Revision: 1.100 $
  *
  * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  * IBM Corporation
@@ -444,6 +444,11 @@
                         if (sch->schib.pmcw.ena)
                                 break;
                 }
+ if (ret == -EBUSY) {
+ struct irb irb;
+ if (tsch(sch->irq, &irb) != 0)
+ break;
+ }
         }
         sprintf (dbf_txt, "ret:%d", ret);
         CIO_TRACE_EVENT (2, dbf_txt);
diff -urN linux-2.6.0-test1/drivers/s390/cio/device.c linux-2.6.0-s390/drivers/s390/cio/device.c
--- linux-2.6.0-test1/drivers/s390/cio/device.c Mon Jul 14 05:36:42 2003
+++ linux-2.6.0-s390/drivers/s390/cio/device.c Thu Jul 17 17:27:33 2003
@@ -1,7 +1,7 @@
 /*
  * drivers/s390/cio/device.c
  * bus driver for ccw devices
- * $Revision: 1.58 $
+ * $Revision: 1.60 $
  *
  * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  * IBM Corporation
@@ -434,6 +434,13 @@
         return ret;
 }
 
+void
+ccw_device_unregister(void *data)
+{
+ device_unregister((struct device *)data);
+}
+
+
 static void
 ccw_device_release(struct device *dev)
 {
@@ -513,17 +520,11 @@
                 wake_up(&ccw_device_init_wq);
 }
 
-static void
+static int
 io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 {
         int rc;
 
- if (!get_device(&sch->dev)) {
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
- return;
- }
-
         sch->dev.driver_data = cdev;
         sch->driver = &io_subchannel_driver;
         cdev->ccwlock = &sch->lock;
@@ -540,9 +541,6 @@
         snprintf (cdev->dev.bus_id, DEVICE_ID_SIZE, "0:%04x",
                   sch->schib.pmcw.dev);
 
- /* Do first half of device_register. */
- device_initialize(&cdev->dev);
-
         /* Increase counter of devices currently in recognition. */
         atomic_inc(&ccw_device_init_count);
 
@@ -551,13 +549,10 @@
         rc = ccw_device_recognition(cdev);
         spin_unlock_irq(cdev->ccwlock);
         if (rc) {
- sch->dev.driver_data = 0;
- put_device(&sch->dev);
- if (cdev->dev.release)
- cdev->dev.release(&cdev->dev);
                 if (atomic_dec_and_test(&ccw_device_init_count))
                         wake_up(&ccw_device_init_wq);
         }
+ return rc;
 }
 
 static int
@@ -565,6 +560,7 @@
 {
         struct subchannel *sch;
         struct ccw_device *cdev;
+ int rc;
 
         sch = to_subchannel(pdev);
         if (sch->dev.driver_data) {
@@ -573,8 +569,20 @@
                  * Register it and exit. This happens for all early
                  * device, e.g. the console.
                  */
- ccw_device_register(sch->dev.driver_data);
+ cdev = sch->dev.driver_data;
+ device_initialize(&cdev->dev);
+ ccw_device_register(cdev);
                 subchannel_add_files(&sch->dev);
+ /*
+ * Check if the device is already online. If it is
+ * the reference count needs to be corrected
+ * (see ccw_device_online and css_init_done for the
+ * ugly details).
+ */
+ if (cdev->private->state != DEV_STATE_NOT_OPER &&
+ cdev->private->state != DEV_STATE_OFFLINE &&
+ cdev->private->state != DEV_STATE_BOXED)
+ get_device(&cdev->dev);
                 return 0;
         }
         cdev = kmalloc (sizeof(*cdev), GFP_KERNEL);
@@ -592,7 +600,23 @@
                 .parent = pdev,
                 .release = ccw_device_release,
         };
- io_subchannel_recog(cdev, to_subchannel(pdev));
+ /* Do first half of device_register. */
+ device_initialize(&cdev->dev);
+
+ if (!get_device(&sch->dev)) {
+ if (cdev->dev.release)
+ cdev->dev.release(&cdev->dev);
+ return 0;
+ }
+
+ rc = io_subchannel_recog(cdev, to_subchannel(pdev));
+ if (rc) {
+ sch->dev.driver_data = 0;
+ put_device(&sch->dev);
+ if (cdev->dev.release)
+ cdev->dev.release(&cdev->dev);
+ }
+
         return 0;
 }
 
@@ -604,6 +628,8 @@
 static int
 ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
 {
+ int rc;
+
         /* Initialize the ccw_device structure. */
         cdev->dev = (struct device) {
                 .parent = &sch->dev,
@@ -613,7 +639,11 @@
                 .parent = &css_bus_device,
                 .bus = &css_bus_type,
         };
- io_subchannel_recog(cdev, sch);
+
+ rc = io_subchannel_recog(cdev, sch);
+ if (rc)
+ return rc;
+
         /* Now wait for the async. recognition to come to an end. */
         while (!dev_fsm_final_state(cdev))
                 wait_cons_dev();
diff -urN linux-2.6.0-test1/drivers/s390/cio/device.h linux-2.6.0-s390/drivers/s390/cio/device.h
--- linux-2.6.0-test1/drivers/s390/cio/device.h Mon Jul 14 05:37:13 2003
+++ linux-2.6.0-s390/drivers/s390/cio/device.h Thu Jul 17 17:27:33 2003
@@ -65,6 +65,8 @@
 
 void io_subchannel_recog_done(struct ccw_device *cdev);
 
+void ccw_device_unregister(void *);
+
 int ccw_device_recognition(struct ccw_device *);
 int ccw_device_online(struct ccw_device *);
 int ccw_device_offline(struct ccw_device *);
diff -urN linux-2.6.0-test1/drivers/s390/cio/device_fsm.c linux-2.6.0-s390/drivers/s390/cio/device_fsm.c
--- linux-2.6.0-test1/drivers/s390/cio/device_fsm.c Mon Jul 14 05:34:32 2003
+++ linux-2.6.0-s390/drivers/s390/cio/device_fsm.c Thu Jul 17 17:27:33 2003
@@ -188,7 +188,7 @@
 
         wake_up(&cdev->private->wait_q);
 
- if (state != DEV_STATE_ONLINE)
+ if (css_init_done && state != DEV_STATE_ONLINE)
                 put_device (&cdev->dev);
 }
 
@@ -293,7 +293,7 @@
         if (cdev->private->state != DEV_STATE_OFFLINE)
                 return -EINVAL;
         sch = to_subchannel(cdev->dev.parent);
- if (!get_device(&cdev->dev))
+ if (css_init_done && !get_device(&cdev->dev))
                 return -ENODEV;
         if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) {
                 /* Couldn't enable the subchannel for i/o. Sick device. */
@@ -384,7 +384,9 @@
 ccw_device_offline_notoper(struct ccw_device *cdev, enum dev_event dev_event)
 {
         cdev->private->state = DEV_STATE_NOT_OPER;
- device_unregister(&cdev->dev);
+ INIT_WORK(&cdev->private->kick_work,
+ ccw_device_unregister, (void *) &cdev->dev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
         wake_up(&cdev->private->wait_q);
 }
 
@@ -403,8 +405,10 @@
                 // FIXME: not-oper indication to device driver ?
                 ccw_device_call_handler(cdev);
         }
+ INIT_WORK(&cdev->private->kick_work,
+ ccw_device_unregister, (void *) &cdev->dev);
+ queue_work(ccw_device_work, &cdev->private->kick_work);
         wake_up(&cdev->private->wait_q);
- device_unregister(&cdev->dev);
 }
 
 /*
diff -urN linux-2.6.0-test1/drivers/s390/cio/qdio.c linux-2.6.0-s390/drivers/s390/cio/qdio.c
--- linux-2.6.0-test1/drivers/s390/cio/qdio.c Mon Jul 14 05:35:14 2003
+++ linux-2.6.0-s390/drivers/s390/cio/qdio.c Thu Jul 17 17:27:33 2003
@@ -55,7 +55,7 @@
 #include "ioasm.h"
 #include "chsc.h"
 
-#define VERSION_QDIO_C "$Revision: 1.51 $"
+#define VERSION_QDIO_C "$Revision: 1.55 $"
 
 /****************** MODULE PARAMETER VARIABLES ********************/
 MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
@@ -1643,6 +1643,7 @@
         default:
                 BUG();
         }
+ ccw_device_set_timeout(cdev, 0);
         wake_up(&cdev->private->wait_q);
 
 }
@@ -1891,26 +1892,25 @@
                 result=-EIO;
                 goto exit;
         }
- /* 4: request block
- * 2: general char
- * 512: chsc char */
- if ((scsc_area->general_char[1] & 0x00800000) != 0x00800000) {
+ /* Check for bit 41. */
+ if ((scsc_area->general_char[1] & 0x00400000) != 0x00400000) {
                 QDIO_PRINT_WARN("Adapter interruption facility not " \
                                 "installed.\n");
                 result=-ENOENT;
                 goto exit;
         }
- if ((scsc_area->chsc_char[2] & 0x00180000) != 0x00180000) {
+ /* Check for bits 107 and 108. */
+ if ((scsc_area->chsc_char[3] & 0x00180000) != 0x00180000) {
                 QDIO_PRINT_WARN("Set Chan Subsys. Char. & Fast-CHSCs " \
                                 "not available.\n");
                 result=-ENOENT;
                 goto exit;
         }
 
- /* Check for hydra thin interrupts. */
+ /* Check for hydra thin interrupts (bit 67). */
         hydra_thinints = ((scsc_area->general_char[2] & 0x10000000)
                 == 0x10000000);
- sprintf(dbf_text,"hydra_ti%1x", hydra_thinints);
+ sprintf(dbf_text,"hydrati%1x", hydra_thinints);
         QDIO_DBF_TEXT0(0,setup,dbf_text);
 exit:
         free_page ((unsigned long) scsc_area);
@@ -2413,8 +2413,10 @@
         QDIO_DBF_TEXT0(0,setup,dbf_text);
         QDIO_DBF_TEXT0(0,trace,dbf_text);
 
- if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat))
+ if (qdio_establish_irq_check_for_errors(cdev, cstat, dstat)) {
+ ccw_device_set_timeout(cdev, 0);
                 return;
+ }
 
         irq_ptr = cdev->private->qdio_data;
 
@@ -2439,7 +2441,7 @@
         qdio_initialize_set_siga_flags_output(irq_ptr);
 
         qdio_set_state(irq_ptr,QDIO_IRQ_STATE_ESTABLISHED);
-
+ ccw_device_set_timeout(cdev, 0);
 }
 
 int
@@ -2698,6 +2700,8 @@
                            "returned %i, next try returned %i\n",
                            irq_ptr->irq,result,result2);
                 result=result2;
+ if (result)
+ ccw_device_set_timeout(cdev, 0);
         }
 
         spin_unlock_irqrestore(get_ccwdev_lock(cdev),saveflags);
@@ -3000,7 +3004,6 @@
                         int buffer_length, int *eof, void *data)
 {
         int c=0;
- int irq;
 
         /* we are always called with buffer_length=4k, so we all
            deliver on the first read */
@@ -3020,7 +3023,7 @@
                  perf_stats.siga_ins);
         _OUTP_IT("Number of SIGA out's issued : %u\n",
                  perf_stats.siga_outs);
- _OUTP_IT("Number of PCIs caught : %u\n",
+ _OUTP_IT("Number of PCIs caught : %u\n",
                  perf_stats.pcis);
         _OUTP_IT("Number of adapter interrupts caught : %u\n",
                  perf_stats.thinints);
@@ -3037,27 +3040,6 @@
                  perf_stats.outbound_cnt);
         _OUTP_IT("\n");
 
- /*
- * FIXME: Rather use driver_for_each_dev, if we had it.
- * I know this loop destroys our layering, but at least gets the
- * performance stats out...
- */
- for (irq=0;irq <= highest_subchannel; irq++) {
- struct qdio_irq *irq_ptr;
- struct ccw_device *cdev;
-
- if (!ioinfo[irq])
- continue;
- cdev = ioinfo[irq]->dev.driver_data;
- if (!cdev)
- continue;
- irq_ptr = cdev->private->qdio_data;
- if (!irq_ptr)
- continue;
- _OUTP_IT("Polling time on irq %4x " \
- ": %u\n",
- irq_ptr->irq,irq_ptr->input_qs[0]->timing.threshold);
- }
         return c;
 }
 
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Jul 23 2003 - 22:00:29 EST