From: Krishna Gudipati <[email protected]>

Change details:
        - Added new attributes to scsi_target and scsi_device structures to
          hold the list of luns to be masked, LUN Masking feature state per LUN
          and per scsi_target.
        - Introduced a new status flag SCSI_SCAN_MASK_LUN, which is used during
          SCSI scan to skip adding a LUN, that is not part of the scsi_target
          LUN mask list and continue scanning rest of the LUNs.
        - Introduced a new structure scsi_mask_lun - which is used to queue the
          luns to be masked to the scsi_target masked_lun_list.
        - Introduced APIs to add or remove LUNs from SCSI target masked_lun_list
          that can be used by the LLD.
        - Introduced an API to dynamically enable/disable LUN Masking per target
        - Made changes to the SCSI scan code to support LUN Masking:
          a) If LUN Masking is enabled on a target, for each newly discovered
             LUN before calling scsi_add_lun() we iterate through the list of
             LUNs that are part of masked_lun_list (to be made visible) and
             invoke scsi_add_lun() only if the LUN is part of the 
masked_lun_list.
          b) LUNs that are not part of the masked_lun_list will be deleted using
             a call to scsi_remove_device().
          c) If a LUN is not part of masked_lun_list and deleted, the routine
             scsi_probe_and_add_lun() will return the status to be 
SCSI_SCAN_MASK_LUN
          d) The status SCSI_SCAN_MASK_LUN will be interpreted as to skip adding
             this LUN and still continue with the scsi scan for rest of the 
LUNs that
             are part of the REPORT_LUNS response.
          e) Made changes to support dynamic LUN masking config change, to 
remove a
             scsi_device already attached or to add a new scsi_device not part 
of the
             initial scan.
        - Made changes to free the populated LUN mask list in 
scsi_target_destroy().
        - Made changes to avoid freeing the scsi_target structure if there are 
no
          devices discovered as part of the scan, since this is the LLD LUN 
Masking
          state holder when LUN Masking is enabled from LLD.

Signed-off-by: Krishna Gudipati <[email protected]>
---
 drivers/scsi/scsi_scan.c   |  251 ++++++++++++++++++++++++++++++++++++++++++--
 include/scsi/scsi.h        |    8 ++
 include/scsi/scsi_device.h |    9 ++
 3 files changed, 259 insertions(+), 9 deletions(-)

diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index d947ffc..1d600687 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -76,6 +76,7 @@
 #define SCSI_SCAN_NO_RESPONSE          0
 #define SCSI_SCAN_TARGET_PRESENT       1
 #define SCSI_SCAN_LUN_PRESENT          2
+#define SCSI_SCAN_MASK_LUN             3
 
 static const char *scsi_null_device_strs = "nullnullnullnull";
 
@@ -318,6 +319,8 @@ static void scsi_target_destroy(struct scsi_target *starget)
 {
        struct device *dev = &starget->dev;
        struct Scsi_Host *shost = dev_to_shost(dev->parent);
+       struct list_head *qe, *qen;
+       struct scsi_mask_lun *mlun;
        unsigned long flags;
 
        transport_destroy_device(dev);
@@ -325,6 +328,14 @@ static void scsi_target_destroy(struct scsi_target 
*starget)
        if (shost->hostt->target_destroy)
                shost->hostt->target_destroy(starget);
        list_del_init(&starget->siblings);
+       if (!list_empty(&starget->masked_lun_list)) {
+               list_for_each_safe(qe, qen, &starget->masked_lun_list) {
+                       mlun = (struct scsi_mask_lun *)qe;
+                       list_del(&mlun->list_entry);
+                       kfree(mlun);
+               }
+               list_del_init(&starget->masked_lun_list);
+       }
        spin_unlock_irqrestore(shost->host_lock, flags);
        put_device(dev);
 }
@@ -411,6 +422,7 @@ static struct scsi_target *scsi_alloc_target(struct device 
*parent,
        starget->can_queue = 0;
        INIT_LIST_HEAD(&starget->siblings);
        INIT_LIST_HEAD(&starget->devices);
+       INIT_LIST_HEAD(&starget->masked_lun_list);
        starget->state = STARGET_CREATED;
        starget->scsi_level = SCSI_2;
        starget->max_target_blocked = SCSI_DEFAULT_TARGET_BLOCKED;
@@ -499,6 +511,160 @@ void scsi_target_reap(struct scsi_target *starget)
 }
 
 /**
+ * scsi_target_mask_lun() - add a LUN to the lun masking (to be visible) list
+ * when lun masking is enabled on this target.
+ * @shost:     Scsi_Host 
+ * @channel:   target channel number (zero if no channels)
+ * @target_id: target id number
+ * @lun:       LUN number to be masked (made visible).
+ *
+ * Description:
+ *     LLD will call this API to add a LUN that needs to be masked/made visible
+ *     The LUN info is queued to the starget->masked_lun_list which is used
+ *     during the SCSI scan to either make this LUN visible or skip adding it
+ *     to the sysfs/remove the device.
+ *
+ * Return value:
+ *     Returns 0 on success.
+ */
+int scsi_target_mask_lun(struct Scsi_Host *shost, int channel,
+                        int target_id, unsigned int lun)
+{
+       struct device *parent = &shost->shost_gendev;
+       struct scsi_target *starget;
+       const int size = sizeof(struct scsi_mask_lun);
+       struct scsi_mask_lun *sm_lun, *mlun;
+       unsigned long flags;
+       unsigned int is_duplicate = 0, ret = 0;
+
+       sm_lun = kzalloc(size, GFP_KERNEL);
+       if (!sm_lun) {
+               printk(KERN_ERR "%s: allocation failure\n", __func__);
+               return -ENOMEM;
+       }
+
+       int_to_scsilun(lun, &sm_lun->mask_lun);
+       spin_lock_irqsave(shost->host_lock, flags);
+       starget = __scsi_find_target(parent, channel, target_id);
+       if (!starget) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               kfree(sm_lun);
+               return -ENXIO;
+       }
+
+       if (list_empty(&starget->masked_lun_list))
+               list_add_tail(&sm_lun->list_entry, &starget->masked_lun_list);
+       else {
+               /* Check for any duplicate entries */
+               list_for_each_entry(mlun, &starget->masked_lun_list, 
list_entry) {
+                       if (mlun && (scsilun_to_int(&mlun->mask_lun) == lun)) {
+                               is_duplicate = 1;
+                               ret = -EEXIST;
+                               break;
+                       }
+               }
+
+               if (!is_duplicate)
+                       list_add_tail(&sm_lun->list_entry, 
&starget->masked_lun_list);
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL(scsi_target_mask_lun);
+
+/**
+ * scsi_target_unmask_lun() - Removes LUN from lun masking (to be visible) list
+ * when lun masking is enabled on this target.
+ * @shost:      Scsi_Host
+ * @channel:    target channel number (zero if no channels)
+ * @target_id:  target id number
+ * @lun:        LUN number to be masked (made visible).
+ *
+ * Description:
+ *     LLD will call this API to remove a LUN that is already present in the
+ *     LUN masking list to make this device invisible in subsequent scan.
+ *     The LUN info is removed to the starget->masked_lun_list which is used
+ *     during the SCSI scan to either make this LUN visible or skip adding it
+ *     to the sysfs/remove the device.
+ *
+ * Return value:
+ *     Returns 0 on success.
+ */
+int scsi_target_unmask_lun(struct Scsi_Host *shost, int channel,
+                          int target_id, unsigned int lun)
+{
+       struct device *parent = &shost->shost_gendev;
+       struct scsi_target *starget;
+       struct scsi_mask_lun *mlun;
+       unsigned long flags;
+       unsigned int found = 0, ret = 0;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       starget = __scsi_find_target(parent, channel, target_id);
+       if (!starget) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               return -ENXIO;
+       }
+
+       if (!list_empty(&starget->masked_lun_list)) {
+               list_for_each_entry(mlun, &starget->masked_lun_list, 
list_entry) {
+                       if (mlun && (scsilun_to_int(&mlun->mask_lun) == lun)) {
+                               found = 1;
+                               list_del(&mlun->list_entry);
+                               break;
+                       }
+               }
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (!found)
+               ret = -ENXIO;
+
+       return ret;
+}
+EXPORT_SYMBOL(scsi_target_unmask_lun);
+
+/**
+ * scsi_target_config_lunmask() - API to enable/disable LUN Masking
+ * @shost:      Scsi_Host
+ * @channel:    target channel number (zero if no channels)
+ * @target_id:  target id number
+ * @masking_config:    1 - enable / 0 - disable LUN masking
+ *
+ * Description:
+ *     LLD will call this API to either enable or disable LUN Masking.
+ *
+ * Return value:
+ *      Returns 0 on success.
+ */
+int scsi_target_config_lunmask(struct Scsi_Host *shost, int channel,
+                              int target_id, int masking_config)
+{
+       struct device *parent = &shost->shost_gendev;
+       struct scsi_target *starget;
+       unsigned int rc = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       starget = __scsi_find_target(parent, channel, target_id);
+       if (!starget) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               rc= -ENXIO;
+               goto out;
+       }
+
+       if (masking_config)
+               starget->is_lm_enabled = 1;
+       else
+               starget->is_lm_enabled = 0;
+       spin_unlock_irqrestore(shost->host_lock, flags);
+out:
+       return rc;
+}
+EXPORT_SYMBOL(scsi_target_config_lunmask);
+
+/**
  * sanitize_inquiry_string - remove non-graphical chars from an INQUIRY result 
string
  * @s: INQUIRY result string to sanitize
  * @len: length of the string
@@ -1005,6 +1171,8 @@ static int scsi_probe_and_add_lun(struct scsi_target 
*starget,
        unsigned char *result;
        int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256;
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       struct scsi_mask_lun *mlun;
+       unsigned long flags;
 
        /*
         * The rescan flag is used as an optimization, the first scan of a
@@ -1012,10 +1180,39 @@ static int scsi_probe_and_add_lun(struct scsi_target 
*starget,
         */
        sdev = scsi_device_lookup_by_target(starget, lun);
        if (sdev) {
+               sdev->is_masked = 0;
                if (rescan || !scsi_device_created(sdev)) {
                        SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
                                "scsi scan: device exists on %s\n",
                                dev_name(&sdev->sdev_gendev)));
+
+                       spin_lock_irqsave(shost->host_lock, flags);
+                       if (starget->is_lm_enabled) {
+                               if (!list_empty(&starget->masked_lun_list)) {
+                                       list_for_each_entry(mlun, 
&starget->masked_lun_list,
+                                                           list_entry) {
+                                               if (mlun && 
(scsilun_to_int(&mlun->mask_lun) == lun)) {
+                                                       sdev->is_masked = 1;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               spin_unlock_irqrestore(shost->host_lock, flags);
+
+                               if (!sdev->is_masked) {
+                                       if (!sdevp)
+                                               scsi_device_put(sdev);
+
+                                       if (bflagsp)
+                                               *bflagsp = 
scsi_get_device_flags(sdev,
+                                                                       
sdev->vendor,
+                                                                       
sdev->model);
+                                       __scsi_remove_device(sdev);
+                                       return SCSI_SCAN_MASK_LUN;
+                               }
+                       } else
+                               spin_unlock_irqrestore(shost->host_lock, flags);
+
                        if (sdevp)
                                *sdevp = sdev;
                        else
@@ -1043,6 +1240,25 @@ static int scsi_probe_and_add_lun(struct scsi_target 
*starget,
 
        if (bflagsp)
                *bflagsp = bflags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (starget->is_lm_enabled) {
+               if (!list_empty(&starget->masked_lun_list)) {
+                       list_for_each_entry(mlun, &starget->masked_lun_list, 
list_entry) {
+                               if (mlun && (scsilun_to_int(&mlun->mask_lun) == 
lun)) {
+                                       sdev->is_masked = 1;
+                                       break;
+                               }
+                       }
+               }
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               if (!sdev->is_masked) {
+                       res = SCSI_SCAN_MASK_LUN;
+                       goto out_free_result;
+               }
+       } else
+               spin_unlock_irqrestore(shost->host_lock, flags);
+
        /*
         * result contains valid SCSI INQUIRY data.
         */
@@ -1152,6 +1368,7 @@ static void scsi_sequential_lun_scan(struct scsi_target 
*starget,
 {
        unsigned int sparse_lun, lun, max_dev_lun;
        struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+       int res = 0;
 
        SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
                                    "%s\n", dev_name(&starget->dev)));
@@ -1208,11 +1425,14 @@ static void scsi_sequential_lun_scan(struct scsi_target 
*starget,
         * until we reach the max, or no LUN is found and we are not
         * sparse_lun.
         */
-       for (lun = 1; lun < max_dev_lun; ++lun)
-               if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
-                                           NULL) != SCSI_SCAN_LUN_PRESENT) &&
-                   !sparse_lun)
+       for (lun = 1; lun < max_dev_lun; ++lun) {
+               res = scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
+                                           NULL);
+               if (res == SCSI_SCAN_MASK_LUN)
+                       continue;
+               else if (res != SCSI_SCAN_LUN_PRESENT && !sparse_lun)
                        return;
+       }
 }
 
 /**
@@ -1474,7 +1694,9 @@ static int scsi_report_lun_scan(struct scsi_target 
*starget, int bflags,
 
                        res = scsi_probe_and_add_lun(starget,
                                lun, NULL, NULL, rescan, NULL);
-                       if (res == SCSI_SCAN_NO_RESPONSE) {
+                       if (res == SCSI_SCAN_MASK_LUN)
+                               continue;
+                       else if (res == SCSI_SCAN_NO_RESPONSE) {
                                /*
                                 * Got some results, but now none, abort.
                                 */
@@ -1567,6 +1789,7 @@ static void __scsi_scan_target(struct device *parent, 
unsigned int channel,
        int bflags = 0;
        int res;
        struct scsi_target *starget;
+       unsigned long flags;
 
        if (shost->this_id == id)
                /*
@@ -1592,7 +1815,8 @@ static void __scsi_scan_target(struct device *parent, 
unsigned int channel,
         * would not configure LUN 0 until all LUNs are scanned.
         */
        res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL);
-       if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) {
+       if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT ||
+           res == SCSI_SCAN_MASK_LUN) {
                if (scsi_report_lun_scan(starget, bflags, rescan) != 0)
                        /*
                         * The REPORT LUN did not scan the target,
@@ -1605,9 +1829,18 @@ 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 */
-       scsi_target_reap(starget);
-
+        * and if not, nuke it
+        *
+        * Note: If LUN Masking is enabled don't delete the starget as this is 
the
+        *       LLD LUN Masking state holder.
+        */
+       if (!starget->is_lm_enabled)
+               scsi_target_reap(starget);
+       else {
+               spin_lock_irqsave(shost->host_lock, flags);
+               starget->reap_ref--;
+               spin_unlock_irqrestore(shost->host_lock, flags);
+       }
        put_device(&starget->dev);
 }
 
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index c6f0974..d0f3b8d 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -361,6 +361,14 @@ struct scsi_lun {
 };
 
 /*
+ * Scsi Mask Lun.
+ */
+struct scsi_mask_lun {
+       struct list_head list_entry;
+       struct scsi_lun mask_lun;
+};
+
+/*
  * The Well Known LUNS (SAM-3) in our int representation of a LUN
  */
 #define SCSI_W_LUN_BASE 0xc100
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 7539f52..6c82f6d 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -155,6 +155,7 @@ struct scsi_device {
        unsigned try_rc_10_first:1;     /* Try READ_CAPACACITY_10 first */
        unsigned is_visible:1;  /* is the device visible in sysfs */
        unsigned wce_default_on:1;      /* Cache is ON by default */
+       unsigned is_masked:1;
 
        DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events 
*/
        struct list_head event_list;    /* asserted events */
@@ -241,6 +242,8 @@ struct scsi_target {
        struct scsi_device      *starget_sdev_user;
        struct list_head        siblings;
        struct list_head        devices;
+       struct list_head        masked_lun_list;
+       unsigned int            is_lm_enabled;
        struct device           dev;
        unsigned int            reap_ref; /* protected by the host lock */
        unsigned int            channel;
@@ -390,6 +393,12 @@ extern int scsi_execute_req(struct scsi_device *sdev, 
const unsigned char *cmd,
                            int data_direction, void *buffer, unsigned bufflen,
                            struct scsi_sense_hdr *, int timeout, int retries,
                            int *resid);
+extern int scsi_target_mask_lun(struct Scsi_Host *shost, int channel,
+                               int target_id, unsigned int lun);
+extern int scsi_target_unmask_lun(struct Scsi_Host *shost, int channel,
+                                 int target_id, unsigned int lun);
+extern int scsi_target_config_lunmask(struct Scsi_Host *shost, int channel,
+                                     int target_id, int masking_config);
 
 #ifdef CONFIG_PM_RUNTIME
 extern int scsi_autopm_get_device(struct scsi_device *);
-- 
1.7.3.rc1

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to