]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - src/patches/suse-2.6.27.39/patches.arch/s390-06-07-cio-attach_detach.patch
Updated kernel (2.6.27.41).
[ipfire-2.x.git] / src / patches / suse-2.6.27.39 / patches.arch / s390-06-07-cio-attach_detach.patch
diff --git a/src/patches/suse-2.6.27.39/patches.arch/s390-06-07-cio-attach_detach.patch b/src/patches/suse-2.6.27.39/patches.arch/s390-06-07-cio-attach_detach.patch
deleted file mode 100644 (file)
index 3c2a81a..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-From: Gerald Schaefer <geraldsc@de.ibm.com>
-Subject: cio: Crashes when repeatetly attaching/detaching devices.
-References: bnc#458339
-
-Symptom:     Oops in dmesg after attaching/detaching a device, subsequent
-             calls to lscss hang.
-Problem:     Incorrect reference counting of subchannel in relation to
-             ccw devices, missing check for delayed registering of ccw
-             devices and incorrectly failing the probe function for I/O
-             subchannels (which leads to unbound subchannels that can't
-             be unregistered).
-Solution:    Make sure that the ccw device holds a reference of the
-             subchannel, that it is not registered if the subchannel is
-             not registered anymore, and schedule unregistering an I/O
-             subchannel if probing encounters an error.
-
-Acked-by: John Jolly <jjolly@suse.de>
----
- drivers/s390/cio/cio.h    |    1 
- drivers/s390/cio/device.c |  128 ++++++++++++++++++++++++++++++++++------------
- 2 files changed, 98 insertions(+), 31 deletions(-)
-
---- linux-sles11.orig/drivers/s390/cio/device.c
-+++ linux-sles11/drivers/s390/cio/device.c
-@@ -716,6 +716,8 @@ ccw_device_release(struct device *dev)
-       struct ccw_device *cdev;
-       cdev = to_ccwdev(dev);
-+      /* Release reference of parent subchannel. */
-+      put_device(cdev->dev.parent);
-       kfree(cdev->private);
-       kfree(cdev);
- }
-@@ -790,37 +792,55 @@ static void sch_attach_disconnected_devi
-       struct subchannel *other_sch;
-       int ret;
--      other_sch = to_subchannel(get_device(cdev->dev.parent));
-+      /* Get reference for new parent. */
-+      if (!get_device(&sch->dev))
-+              return;
-+      other_sch = to_subchannel(cdev->dev.parent);
-+      /* Note: device_move() changes cdev->dev.parent */
-       ret = device_move(&cdev->dev, &sch->dev);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving disconnected device 0.%x.%04x failed "
-                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
--              put_device(&other_sch->dev);
-+              /* Put reference for new parent. */
-+              put_device(&sch->dev);
-               return;
-       }
-       sch_set_cdev(other_sch, NULL);
-       /* No need to keep a subchannel without ccw device around. */
-       css_sch_device_unregister(other_sch);
--      put_device(&other_sch->dev);
-       sch_attach_device(sch, cdev);
-+      /* Put reference for old parent. */
-+      put_device(&other_sch->dev);
- }
- static void sch_attach_orphaned_device(struct subchannel *sch,
-                                      struct ccw_device *cdev)
- {
-       int ret;
-+      struct subchannel *pseudo_sch;
--      /* Try to move the ccw device to its new subchannel. */
-+      /* Get reference for new parent. */
-+      if (!get_device(&sch->dev))
-+              return;
-+      pseudo_sch = to_subchannel(cdev->dev.parent);
-+      /*
-+       * Try to move the ccw device to its new subchannel.
-+       * Note: device_move() changes cdev->dev.parent
-+       */
-       ret = device_move(&cdev->dev, &sch->dev);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x from orphanage "
-                             "failed (ret=%d)!\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-+              /* Put reference for new parent. */
-+              put_device(&sch->dev);
-               return;
-       }
-       sch_attach_device(sch, cdev);
-+      /* Put reference on pseudo subchannel. */
-+      put_device(&pseudo_sch->dev);
- }
- static void sch_create_and_recog_new_device(struct subchannel *sch)
-@@ -842,9 +862,11 @@ static void sch_create_and_recog_new_dev
-               spin_lock_irq(sch->lock);
-               sch_set_cdev(sch, NULL);
-               spin_unlock_irq(sch->lock);
--              if (cdev->dev.release)
--                      cdev->dev.release(&cdev->dev);
-               css_sch_device_unregister(sch);
-+              /* Put reference from io_subchannel_create_ccwdev(). */
-+              put_device(&sch->dev);
-+              /* Give up initial reference. */
-+              put_device(&cdev->dev);
-       }
- }
-@@ -866,15 +888,20 @@ void ccw_device_move_to_orphanage(struct
-       dev_id.devno = sch->schib.pmcw.dev;
-       dev_id.ssid = sch->schid.ssid;
-+      /* Increase refcount for pseudo subchannel. */
-+      get_device(&css->pseudo_subchannel->dev);
-       /*
-        * Move the orphaned ccw device to the orphanage so the replacing
-        * ccw device can take its place on the subchannel.
-+       * Note: device_move() changes cdev->dev.parent
-        */
-       ret = device_move(&cdev->dev, &css->pseudo_subchannel->dev);
-       if (ret) {
-               CIO_MSG_EVENT(0, "Moving device 0.%x.%04x to orphanage failed "
-                             "(ret=%d)!\n", cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
-+              /* Decrease refcount for pseudo subchannel again. */
-+              put_device(&css->pseudo_subchannel->dev);
-               return;
-       }
-       cdev->ccwlock = css->pseudo_subchannel->lock;
-@@ -886,14 +913,20 @@ void ccw_device_move_to_orphanage(struct
-       replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev);
-       if (replacing_cdev) {
-               sch_attach_disconnected_device(sch, replacing_cdev);
-+              /* Release reference of subchannel from old cdev. */
-+              put_device(&sch->dev);
-               return;
-       }
-       replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id);
-       if (replacing_cdev) {
-               sch_attach_orphaned_device(sch, replacing_cdev);
-+              /* Release reference of subchannel from old cdev. */
-+              put_device(&sch->dev);
-               return;
-       }
-       sch_create_and_recog_new_device(sch);
-+      /* Release reference of subchannel from old cdev. */
-+      put_device(&sch->dev);
- }
- /*
-@@ -911,6 +944,14 @@ io_subchannel_register(struct work_struc
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       cdev = priv->cdev;
-       sch = to_subchannel(cdev->dev.parent);
-+      /*
-+       * Check if subchannel is still registered. It may have become
-+       * unregistered if a machine check hit us after finishing
-+       * device recognition but before the register work could be
-+       * queued.
-+       */
-+      if (!device_is_registered(&sch->dev))
-+              goto out_err;
-       css_update_ssd_info(sch);
-       /*
-        * io_subchannel_register() will also be called after device
-@@ -942,22 +983,19 @@ io_subchannel_register(struct work_struc
-               CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
-                             cdev->private->dev_id.ssid,
-                             cdev->private->dev_id.devno, ret);
--              put_device(&cdev->dev);
-               spin_lock_irqsave(sch->lock, flags);
-               sch_set_cdev(sch, NULL);
-               spin_unlock_irqrestore(sch->lock, flags);
--              kfree (cdev->private);
--              kfree (cdev);
--              put_device(&sch->dev);
--              if (atomic_dec_and_test(&ccw_device_init_count))
--                      wake_up(&ccw_device_init_wq);
--              return;
-+              /* Release initial device reference. */
-+              put_device(&cdev->dev);
-+              goto out_err;
-       }
--      put_device(&cdev->dev);
- out:
-       cdev->private->flags.recog_done = 1;
--      put_device(&sch->dev);
-       wake_up(&cdev->private->wait_q);
-+out_err:
-+      /* Release reference for workqueue processing. */
-+      put_device(&cdev->dev);
-       if (atomic_dec_and_test(&ccw_device_init_count))
-               wake_up(&ccw_device_init_wq);
- }
-@@ -1068,10 +1106,15 @@ static void ccw_device_move_to_sch(struc
-       priv = container_of(work, struct ccw_device_private, kick_work);
-       sch = priv->sch;
-       cdev = priv->cdev;
--      former_parent = ccw_device_is_orphan(cdev) ?
--              NULL : to_subchannel(get_device(cdev->dev.parent));
-+      former_parent = to_subchannel(cdev->dev.parent);
-+      /* Get reference for new parent. */
-+      if (!get_device(&sch->dev))
-+              return;
-       mutex_lock(&sch->reg_mutex);
--      /* Try to move the ccw device to its new subchannel. */
-+      /*
-+       * Try to move the ccw device to its new subchannel.
-+       * Note: device_move() changes cdev->dev.parent
-+       */
-       rc = device_move(&cdev->dev, &sch->dev);
-       mutex_unlock(&sch->reg_mutex);
-       if (rc) {
-@@ -1081,9 +1124,11 @@ static void ccw_device_move_to_sch(struc
-                             cdev->private->dev_id.devno, sch->schid.ssid,
-                             sch->schid.sch_no, rc);
-               css_sch_device_unregister(sch);
-+              /* Put reference for new parent again. */
-+              put_device(&sch->dev);
-               goto out;
-       }
--      if (former_parent) {
-+      if (!sch_is_pseudo_sch(former_parent)) {
-               spin_lock_irq(former_parent->lock);
-               sch_set_cdev(former_parent, NULL);
-               spin_unlock_irq(former_parent->lock);
-@@ -1094,8 +1139,8 @@ static void ccw_device_move_to_sch(struc
-       }
-       sch_attach_device(sch, cdev);
- out:
--      if (former_parent)
--              put_device(&former_parent->dev);
-+      /* Put reference for old parent. */
-+      put_device(&former_parent->dev);
-       put_device(&cdev->dev);
- }
-@@ -1137,6 +1182,30 @@ static void io_subchannel_init_fields(st
-       sch->schib.mba = 0;
- }
-+static void io_subchannel_do_unreg(struct work_struct *work)
-+{
-+      struct subchannel *sch;
-+
-+      sch = container_of(work, struct subchannel, work);
-+      css_sch_device_unregister(sch);
-+      /* Reset intparm to zeroes. */
-+      sch->schib.pmcw.intparm = 0;
-+      cio_modify(sch);
-+      put_device(&sch->dev);
-+}
-+
-+/* Schedule unregister if we have no cdev. */
-+static void io_subchannel_schedule_removal(struct subchannel *sch)
-+{
-+      get_device(&sch->dev);
-+      INIT_WORK(&sch->work, io_subchannel_do_unreg);
-+      queue_work(slow_path_wq, &sch->work);
-+}
-+
-+/*
-+ * Note: We always return 0 so that we bind to the device even on error.
-+ * This is needed so that our remove function is called on unregister.
-+ */
- static int io_subchannel_probe(struct subchannel *sch)
- {
-       struct ccw_device *cdev;
-@@ -1186,14 +1255,12 @@ static int io_subchannel_probe(struct su
-       rc = sysfs_create_group(&sch->dev.kobj,
-                               &io_subchannel_attr_group);
-       if (rc)
--              return rc;
-+              goto out_schedule;
-       /* Allocate I/O subchannel private data. */
-       sch->private = kzalloc(sizeof(struct io_subchannel_private),
-                              GFP_KERNEL | GFP_DMA);
--      if (!sch->private) {
--              rc = -ENOMEM;
-+      if (!sch->private)
-               goto out_err;
--      }
-       cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
-       if (!cdev)
-               cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
-@@ -1211,24 +1278,23 @@ static int io_subchannel_probe(struct su
-               return 0;
-       }
-       cdev = io_subchannel_create_ccwdev(sch);
--      if (IS_ERR(cdev)) {
--              rc = PTR_ERR(cdev);
-+      if (IS_ERR(cdev))
-               goto out_err;
--      }
-       rc = io_subchannel_recog(cdev, sch);
-       if (rc) {
-               spin_lock_irqsave(sch->lock, flags);
-               sch_set_cdev(sch, NULL);
-+              io_subchannel_recog_done(cdev);
-               spin_unlock_irqrestore(sch->lock, flags);
--              if (cdev->dev.release)
--                      cdev->dev.release(&cdev->dev);
--              goto out_err;
-       }
-       return 0;
- out_err:
-       kfree(sch->private);
-       sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
-       return rc;
-+out_schedule:
-+      io_subchannel_schedule_removal(sch);
-+      return 0;
- }
- static int
---- linux-sles11.orig/drivers/s390/cio/cio.h
-+++ linux-sles11/drivers/s390/cio/cio.h
-@@ -82,6 +82,7 @@ struct subchannel {
-       struct device dev;      /* entry in device tree */
-       struct css_driver *driver;
-       void *private; /* private per subchannel type data */
-+      struct work_struct work;
- } __attribute__ ((aligned(8)));
- #define IO_INTERRUPT_TYPE        0 /* I/O interrupt type */