From: Krishna Gudipati <kgudipat@xxxxxxxxxxx>
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 <kgudipat@xxxxxxxxxxx>
---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
[SCSI Target Devel]
[Linux SCSI Target Infrastructure]
[Kernel Newbies]
[Share Photos]
[IDE]
[Security]
[Git]
[Netfilter]
[Bugtraq]
[Photos]
[Yosemite]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Linux ATA RAID]
[Linux IIO]
[Samba]
[Video 4 Linux]
[Device Mapper]
[Linux Resources]