]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.10-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 13 May 2014 23:00:23 +0000 (16:00 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 13 May 2014 23:00:23 +0000 (16:00 -0700)
added patches:
scsi-dual-scan-thread-bug-fix.patch
scsi-fix-our-current-target-reap-infrastructure.patch
scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch

queue-3.10/scsi-dual-scan-thread-bug-fix.patch [new file with mode: 0644]
queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch [new file with mode: 0644]
queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch [new file with mode: 0644]
queue-3.10/series [new file with mode: 0644]
queue-3.14/series [new file with mode: 0644]
queue-3.4/series [new file with mode: 0644]

diff --git a/queue-3.10/scsi-dual-scan-thread-bug-fix.patch b/queue-3.10/scsi-dual-scan-thread-bug-fix.patch
new file mode 100644 (file)
index 0000000..266ddaf
--- /dev/null
@@ -0,0 +1,73 @@
+From f2495e228fce9f9cec84367547813cbb0d6db15a Mon Sep 17 00:00:00 2001
+From: James Bottomley <JBottomley@Parallels.com>
+Date: Tue, 21 Jan 2014 07:01:41 -0800
+Subject: SCSI: dual scan thread bug fix
+
+From: James Bottomley <JBottomley@Parallels.com>
+
+commit f2495e228fce9f9cec84367547813cbb0d6db15a upstream.
+
+In the highly unusual case where two threads are running concurrently through
+the scanning code scanning the same target, we run into the situation where
+one may allocate the target while the other is still using it.  In this case,
+because the reap checks for STARGET_CREATED and kills the target without
+reference counting, the second thread will do the wrong thing on reap.
+
+Fix this by reference counting even creates and doing the STARGET_CREATED
+check in the final put.
+
+Tested-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: James Bottomley <JBottomley@Parallels.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/scsi/scsi_scan.c |   23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -320,6 +320,7 @@ static void scsi_target_destroy(struct s
+       struct Scsi_Host *shost = dev_to_shost(dev->parent);
+       unsigned long flags;
++      starget->state = STARGET_DEL;
+       transport_destroy_device(dev);
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (shost->hostt->target_destroy)
+@@ -384,9 +385,15 @@ static void scsi_target_reap_ref_release
+       struct scsi_target *starget
+               = container_of(kref, struct scsi_target, reap_ref);
+-      transport_remove_device(&starget->dev);
+-      device_del(&starget->dev);
+-      starget->state = STARGET_DEL;
++      /*
++       * if we get here and the target is still in the CREATED state that
++       * means it was allocated but never made visible (because a scan
++       * turned up no LUNs), so don't call device_del() on it.
++       */
++      if (starget->state != STARGET_CREATED) {
++              transport_remove_device(&starget->dev);
++              device_del(&starget->dev);
++      }
+       scsi_target_destroy(starget);
+ }
+@@ -506,11 +513,13 @@ static struct scsi_target *scsi_alloc_ta
+  */
+ void scsi_target_reap(struct scsi_target *starget)
+ {
++      /*
++       * serious problem if this triggers: STARGET_DEL is only set in the if
++       * the reap_ref drops to zero, so we're trying to do another final put
++       * on an already released kref
++       */
+       BUG_ON(starget->state == STARGET_DEL);
+-      if (starget->state == STARGET_CREATED)
+-              scsi_target_destroy(starget);
+-      else
+-              scsi_target_reap_ref_put(starget);
++      scsi_target_reap_ref_put(starget);
+ }
+ /**
diff --git a/queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch b/queue-3.10/scsi-fix-our-current-target-reap-infrastructure.patch
new file mode 100644 (file)
index 0000000..0a1d893
--- /dev/null
@@ -0,0 +1,272 @@
+From e63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 Mon Sep 17 00:00:00 2001
+From: James Bottomley <JBottomley@Parallels.com>
+Date: Tue, 21 Jan 2014 07:00:50 -0800
+Subject: scsi: fix our current target reap infrastructure
+
+From: James Bottomley <JBottomley@Parallels.com>
+
+commit e63ed0d7a98014fdfc2cfeb3f6dada313dcabb59 upstream.
+
+This patch eliminates the reap_ref and replaces it with a proper kref.
+On last put of this kref, the target is removed from visibility in
+sysfs.  The final call to scsi_target_reap() for the device is done from
+__scsi_remove_device() and only if the device was made visible.  This
+ensures that the target disappears as soon as the last device is gone
+rather than waiting until final release of the device (which is often
+too long).
+
+Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
+Tested-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: James Bottomley <JBottomley@Parallels.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/scsi/scsi_scan.c   |   99 +++++++++++++++++++++++++++------------------
+ drivers/scsi/scsi_sysfs.c  |   20 ++++++---
+ include/scsi/scsi_device.h |    3 -
+ 3 files changed, 75 insertions(+), 47 deletions(-)
+
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -371,6 +371,31 @@ static struct scsi_target *__scsi_find_t
+ }
+ /**
++ * scsi_target_reap_ref_release - remove target from visibility
++ * @kref: the reap_ref in the target being released
++ *
++ * Called on last put of reap_ref, which is the indication that no device
++ * under this target is visible anymore, so render the target invisible in
++ * sysfs.  Note: we have to be in user context here because the target reaps
++ * should be done in places where the scsi device visibility is being removed.
++ */
++static void scsi_target_reap_ref_release(struct kref *kref)
++{
++      struct scsi_target *starget
++              = container_of(kref, struct scsi_target, reap_ref);
++
++      transport_remove_device(&starget->dev);
++      device_del(&starget->dev);
++      starget->state = STARGET_DEL;
++      scsi_target_destroy(starget);
++}
++
++static void scsi_target_reap_ref_put(struct scsi_target *starget)
++{
++      kref_put(&starget->reap_ref, scsi_target_reap_ref_release);
++}
++
++/**
+  * scsi_alloc_target - allocate a new or find an existing target
+  * @parent:   parent of the target (need not be a scsi host)
+  * @channel:  target channel number (zero if no channels)
+@@ -392,7 +417,7 @@ static struct scsi_target *scsi_alloc_ta
+               + shost->transportt->target_size;
+       struct scsi_target *starget;
+       struct scsi_target *found_target;
+-      int error;
++      int error, ref_got;
+       starget = kzalloc(size, GFP_KERNEL);
+       if (!starget) {
+@@ -401,7 +426,7 @@ static struct scsi_target *scsi_alloc_ta
+       }
+       dev = &starget->dev;
+       device_initialize(dev);
+-      starget->reap_ref = 1;
++      kref_init(&starget->reap_ref);
+       dev->parent = get_device(parent);
+       dev_set_name(dev, "target%d:%d:%d", shost->host_no, channel, id);
+       dev->bus = &scsi_bus_type;
+@@ -441,29 +466,36 @@ static struct scsi_target *scsi_alloc_ta
+       return starget;
+  found:
+-      found_target->reap_ref++;
++      /*
++       * release routine already fired if kref is zero, so if we can still
++       * take the reference, the target must be alive.  If we can't, it must
++       * be dying and we need to wait for a new target
++       */
++      ref_got = kref_get_unless_zero(&found_target->reap_ref);
++
+       spin_unlock_irqrestore(shost->host_lock, flags);
+-      if (found_target->state != STARGET_DEL) {
++      if (ref_got) {
+               put_device(dev);
+               return found_target;
+       }
+-      /* Unfortunately, we found a dying target; need to
+-       * wait until it's dead before we can get a new one */
++      /*
++       * Unfortunately, we found a dying target; need to wait until it's
++       * dead before we can get a new one.  There is an anomaly here.  We
++       * *should* call scsi_target_reap() to balance the kref_get() of the
++       * reap_ref above.  However, since the target being released, it's
++       * already invisible and the reap_ref is irrelevant.  If we call
++       * scsi_target_reap() we might spuriously do another device_del() on
++       * an already invisible target.
++       */
+       put_device(&found_target->dev);
+-      flush_scheduled_work();
++      /*
++       * length of time is irrelevant here, we just want to yield the CPU
++       * for a tick to avoid busy waiting for the target to die.
++       */
++      msleep(1);
+       goto retry;
+ }
+-static void scsi_target_reap_usercontext(struct work_struct *work)
+-{
+-      struct scsi_target *starget =
+-              container_of(work, struct scsi_target, ew.work);
+-
+-      transport_remove_device(&starget->dev);
+-      device_del(&starget->dev);
+-      scsi_target_destroy(starget);
+-}
+-
+ /**
+  * scsi_target_reap - check to see if target is in use and destroy if not
+  * @starget: target to be checked
+@@ -474,28 +506,11 @@ static void scsi_target_reap_usercontext
+  */
+ void scsi_target_reap(struct scsi_target *starget)
+ {
+-      struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+-      unsigned long flags;
+-      enum scsi_target_state state;
+-      int empty = 0;
+-
+-      spin_lock_irqsave(shost->host_lock, flags);
+-      state = starget->state;
+-      if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
+-              empty = 1;
+-              starget->state = STARGET_DEL;
+-      }
+-      spin_unlock_irqrestore(shost->host_lock, flags);
+-
+-      if (!empty)
+-              return;
+-
+-      BUG_ON(state == STARGET_DEL);
+-      if (state == STARGET_CREATED)
++      BUG_ON(starget->state == STARGET_DEL);
++      if (starget->state == STARGET_CREATED)
+               scsi_target_destroy(starget);
+       else
+-              execute_in_process_context(scsi_target_reap_usercontext,
+-                                         &starget->ew);
++              scsi_target_reap_ref_put(starget);
+ }
+ /**
+@@ -1527,6 +1542,10 @@ struct scsi_device *__scsi_add_device(st
+       }
+       mutex_unlock(&shost->scan_mutex);
+       scsi_autopm_put_target(starget);
++      /*
++       * paired with scsi_alloc_target().  Target will be destroyed unless
++       * scsi_probe_and_add_lun made an underlying device visible
++       */
+       scsi_target_reap(starget);
+       put_device(&starget->dev);
+@@ -1607,8 +1626,10 @@ static void __scsi_scan_target(struct de
+  out_reap:
+       scsi_autopm_put_target(starget);
+-      /* now determine if the target has any children at all
+-       * and if not, nuke it */
++      /*
++       * paired with scsi_alloc_target(): determine if the target has
++       * any children at all and if not, nuke it
++       */
+       scsi_target_reap(starget);
+       put_device(&starget->dev);
+--- a/drivers/scsi/scsi_sysfs.c
++++ b/drivers/scsi/scsi_sysfs.c
+@@ -332,17 +332,14 @@ static void scsi_device_dev_release_user
+ {
+       struct scsi_device *sdev;
+       struct device *parent;
+-      struct scsi_target *starget;
+       struct list_head *this, *tmp;
+       unsigned long flags;
+       sdev = container_of(work, struct scsi_device, ew.work);
+       parent = sdev->sdev_gendev.parent;
+-      starget = to_scsi_target(parent);
+       spin_lock_irqsave(sdev->host->host_lock, flags);
+-      starget->reap_ref++;
+       list_del(&sdev->siblings);
+       list_del(&sdev->same_target_siblings);
+       list_del(&sdev->starved_entry);
+@@ -362,8 +359,6 @@ static void scsi_device_dev_release_user
+       /* NULL queue means the device can't be used */
+       sdev->request_queue = NULL;
+-      scsi_target_reap(scsi_target(sdev));
+-
+       kfree(sdev->inquiry);
+       kfree(sdev);
+@@ -978,6 +973,13 @@ void __scsi_remove_device(struct scsi_de
+               sdev->host->hostt->slave_destroy(sdev);
+       transport_destroy_device(dev);
++      /*
++       * Paired with the kref_get() in scsi_sysfs_initialize().  We have
++       * remoed sysfs visibility from the device, so make the target
++       * invisible if this was the last device underneath it.
++       */
++      scsi_target_reap(scsi_target(sdev));
++
+       put_device(dev);
+ }
+@@ -1040,7 +1042,7 @@ void scsi_remove_target(struct device *d
+                       continue;
+               if (starget->dev.parent == dev || &starget->dev == dev) {
+                       /* assuming new targets arrive at the end */
+-                      starget->reap_ref++;
++                      kref_get(&starget->reap_ref);
+                       spin_unlock_irqrestore(shost->host_lock, flags);
+                       if (last)
+                               scsi_target_reap(last);
+@@ -1124,6 +1126,12 @@ void scsi_sysfs_device_initialize(struct
+       list_add_tail(&sdev->same_target_siblings, &starget->devices);
+       list_add_tail(&sdev->siblings, &shost->__devices);
+       spin_unlock_irqrestore(shost->host_lock, flags);
++      /*
++       * device can now only be removed via __scsi_remove_device() so hold
++       * the target.  Target will be held in CREATED state until something
++       * beneath it becomes visible (in which case it moves to RUNNING)
++       */
++      kref_get(&starget->reap_ref);
+ }
+ int scsi_is_sdev_device(const struct device *dev)
+--- a/include/scsi/scsi_device.h
++++ b/include/scsi/scsi_device.h
+@@ -248,7 +248,7 @@ struct scsi_target {
+       struct list_head        siblings;
+       struct list_head        devices;
+       struct device           dev;
+-      unsigned int            reap_ref; /* protected by the host lock */
++      struct kref             reap_ref; /* last put renders target invisible */
+       unsigned int            channel;
+       unsigned int            id; /* target id ... replace
+                                    * scsi_device.id eventually */
+@@ -272,7 +272,6 @@ struct scsi_target {
+ #define SCSI_DEFAULT_TARGET_BLOCKED   3
+       char                    scsi_level;
+-      struct execute_work     ew;
+       enum scsi_target_state  state;
+       void                    *hostdata; /* available to low-level driver */
+       unsigned long           starget_data[0]; /* for the transport */
diff --git a/queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch b/queue-3.10/scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch
new file mode 100644 (file)
index 0000000..d095cf2
--- /dev/null
@@ -0,0 +1,34 @@
+From 3de2260140417759c669d391613d583baf03b0cf Mon Sep 17 00:00:00 2001
+From: Dan Carpenter <dan.carpenter@oracle.com>
+Date: Wed, 30 Oct 2013 20:13:51 +0300
+Subject: SCSI: megaraid: missing bounds check in mimd_to_kioc()
+
+From: Dan Carpenter <dan.carpenter@oracle.com>
+
+commit 3de2260140417759c669d391613d583baf03b0cf upstream.
+
+pthru32->dataxferlen comes from the user so we need to check that it's
+not too large so we don't overflow the buffer.
+
+Reported-by: Nico Golde <nico@ngolde.de>
+Reported-by: Fabian Yamaguchi <fabs@goesec.de>
+Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
+Acked-by: Sumit Saxena <sumit.saxena@lsi.com>
+Signed-off-by: James Bottomley <JBottomley@Parallels.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/scsi/megaraid/megaraid_mm.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/scsi/megaraid/megaraid_mm.c
++++ b/drivers/scsi/megaraid/megaraid_mm.c
+@@ -486,6 +486,8 @@ mimd_to_kioc(mimd_t __user *umimd, mraid
+       pthru32->dataxferaddr   = kioc->buf_paddr;
+       if (kioc->data_dir & UIOC_WR) {
++              if (pthru32->dataxferlen > kioc->xferlen)
++                      return -EINVAL;
+               if (copy_from_user(kioc->buf_vaddr, kioc->user_data,
+                                               pthru32->dataxferlen)) {
+                       return (-EFAULT);
diff --git a/queue-3.10/series b/queue-3.10/series
new file mode 100644 (file)
index 0000000..3ec2493
--- /dev/null
@@ -0,0 +1,3 @@
+scsi-fix-our-current-target-reap-infrastructure.patch
+scsi-dual-scan-thread-bug-fix.patch
+scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch
diff --git a/queue-3.14/series b/queue-3.14/series
new file mode 100644 (file)
index 0000000..3ec2493
--- /dev/null
@@ -0,0 +1,3 @@
+scsi-fix-our-current-target-reap-infrastructure.patch
+scsi-dual-scan-thread-bug-fix.patch
+scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch
diff --git a/queue-3.4/series b/queue-3.4/series
new file mode 100644 (file)
index 0000000..07f65a4
--- /dev/null
@@ -0,0 +1 @@
+scsi-megaraid-missing-bounds-check-in-mimd_to_kioc.patch