rk fb: from rk3368 fb only need to reserved 1 framebuffer
[firefly-linux-kernel-4.4.55.git] / drivers / scsi / scsi_scan.c
index c6c80c9c96015dbdc8d42a31b401ed6d7b84d832..859240408f9ee4ce7a04f9ae767153a933d1ba6d 100644 (file)
@@ -184,18 +184,6 @@ int scsi_complete_async_scans(void)
        return 0;
 }
 
-/* Only exported for the benefit of scsi_wait_scan */
-EXPORT_SYMBOL_GPL(scsi_complete_async_scans);
-
-#ifndef MODULE
-/*
- * For async scanning we need to wait for all the scans to complete before
- * trying to mount the root fs.  Otherwise non-modular drivers may not be ready
- * yet.
- */
-late_initcall(scsi_complete_async_scans);
-#endif
-
 /**
  * scsi_unlock_floptical - unlock device via a special MODE SENSE command
  * @sdev:      scsi device to send command to
@@ -297,7 +285,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
                kfree(sdev);
                goto out;
        }
-       blk_get_queue(sdev->request_queue);
+       WARN_ON_ONCE(!blk_get_queue(sdev->request_queue));
        sdev->request_queue->queuedata = sdev;
        scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
 
@@ -332,6 +320,7 @@ static void scsi_target_destroy(struct scsi_target *starget)
        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)
@@ -382,6 +371,37 @@ static struct scsi_target *__scsi_find_target(struct device *parent,
        return found_starget;
 }
 
+/**
+ * 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);
+
+       /*
+        * 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);
+}
+
+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)
@@ -404,7 +424,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
                + 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) {
@@ -413,7 +433,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
        }
        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;
@@ -453,29 +473,36 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
        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
@@ -486,28 +513,13 @@ static void scsi_target_reap_usercontext(struct work_struct *work)
  */
 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)
-               scsi_target_destroy(starget);
-       else
-               execute_in_process_context(scsi_target_reap_usercontext,
-                                          &starget->ew);
+       /*
+        * 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);
+       scsi_target_reap_ref_put(starget);
 }
 
 /**
@@ -933,6 +945,9 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
        if (*bflags & BLIST_RETRY_HWERROR)
                sdev->retry_hwerror = 1;
 
+       if (*bflags & BLIST_NO_DIF)
+               sdev->no_dif = 1;
+
        transport_configure_device(&sdev->sdev_gendev);
 
        if (sdev->host->hostt->slave_configure) {
@@ -1305,6 +1320,7 @@ EXPORT_SYMBOL(int_to_scsilun);
  *   LUNs even if it's older than SCSI-3.
  *   If BLIST_NOREPORTLUN is set, return 1 always.
  *   If BLIST_NOLUN is set, return 0 always.
+ *   If starget->no_report_luns is set, return 1 always.
  *
  * Return:
  *     0: scan completed (or no memory, so further scanning is futile)
@@ -1331,6 +1347,7 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
         * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
         * Also allow SCSI-2 if BLIST_REPORTLUN2 is set and host adapter does
         * support more than 8 LUNs.
+        * Don't attempt if the target doesn't support REPORT LUNS.
         */
        if (bflags & BLIST_NOREPORTLUN)
                return 1;
@@ -1342,6 +1359,8 @@ static int scsi_report_lun_scan(struct scsi_target *starget, int bflags,
                return 1;
        if (bflags & BLIST_NOLUN)
                return 0;
+       if (starget->no_report_luns)
+               return 1;
 
        if (!(sdev = scsi_device_lookup_by_target(starget, 0))) {
                sdev = scsi_alloc_sdev(starget, 0, NULL);
@@ -1532,6 +1551,10 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
        }
        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);
 
@@ -1612,8 +1635,10 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel,
 
  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);
@@ -1848,14 +1873,13 @@ static void do_scsi_scan_host(struct Scsi_Host *shost)
        }
 }
 
-static int do_scan_async(void *_data)
+static void do_scan_async(void *_data, async_cookie_t c)
 {
        struct async_scan_data *data = _data;
        struct Scsi_Host *shost = data->shost;
 
        do_scsi_scan_host(shost);
        scsi_finish_async_scan(data);
-       return 0;
 }
 
 /**
@@ -1864,7 +1888,6 @@ static int do_scan_async(void *_data)
  **/
 void scsi_scan_host(struct Scsi_Host *shost)
 {
-       struct task_struct *p;
        struct async_scan_data *data;
 
        if (strncmp(scsi_scan_type, "none", 4) == 0)
@@ -1879,9 +1902,11 @@ void scsi_scan_host(struct Scsi_Host *shost)
                return;
        }
 
-       p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
-       if (IS_ERR(p))
-               do_scan_async(data);
+       /* register with the async subsystem so wait_for_device_probe()
+        * will flush this work
+        */
+       async_schedule(do_scan_async, data);
+
        /* scsi_autopm_put_host(shost) is called in scsi_finish_async_scan() */
 }
 EXPORT_SYMBOL(scsi_scan_host);