Commit 1a47401b authored by Matthew R. Ochs's avatar Matthew R. Ochs Committed by James Bottomley

cxlflash: Fix to avoid corrupting port selection mask

The port selection mask of a LUN can be corrupted when the manage LUN
ioctl (DK_CXLFLASH_MANAGE_LUN) is issued more than once for any device.

This mask indicates to the AFU which port[s] can be used for a data
transfer to/from a particular LUN. The mask is critical to ensuring the
correct behavior when using the virtual LUN function of this adapter.
When the mask is configured for both ports, an I/O may be sent to either
port as the AFU assumes that each port has access to the same physical
device (specified by LUN ID in the port LUN table).

In a situation where the mask becomes incorrectly configured to reflect
access to both ports when in fact there is only access through a single
port, an I/O can be targeted to the wrong physical device. This can lead
to data corruption among other ill effects (e.g. security leaks).

The cause for this corruption is the assumption that the ioctl will only
be called a second time for a LUN when it is being configured for access
via a second port. A boolean 'newly_created' variable is used to
differentiate between a LUN that was created (and subsequently configured
for single port access) and one that is destined for access across both
ports. While initially set to 'true', this sticky boolean is toggled to
the 'false' state during a lookup on any next ioctl performed on a device
with a matching WWN/WWID. The code fails to realize that the match could
in fact be the same device calling in again. From here, an assumption is
made that any LUN with 'newly_created' set to 'false' is configured for
access over both ports and the port selection mask is set to reflect this.
Any future attempts to use this LUN for hosting a virtual LUN will result
in the port LUN table being incorrectly programmed.

As a remedy, the 'newly_created' concept was removed entirely and replaced
with code that always constructs the port selection mask based upon the
SCSI channel of the LUN being accessed. The bits remain sticky, therefore
allowing for a device to be accessed over both ports when that is in fact
the correct physical configuration.

Also included in this commit are a few minor related changes to enhance
the fix and provide better debug information for port selection mask and
port LUN table bugs in the future. These include renaming refresh_local()
to lookup_local(), tracing the WWN/WWID as a big-endian entity, and
tracing the port selection mask, SCSI channel, and LUN ID each time the
port LUN table is programmed.
Signed-off-by: default avatarMatthew R. Ochs <mrochs@linux.vnet.ibm.com>
Acked-by: default avatarManoj Kumar <manoj@linux.vnet.ibm.com>
Reviewed-by: default avatarAndrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Odin.com>
parent e6e6df3f
...@@ -41,7 +41,6 @@ static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid) ...@@ -41,7 +41,6 @@ static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid)
} }
lli->sdev = sdev; lli->sdev = sdev;
lli->newly_created = true;
lli->host_no = sdev->host->host_no; lli->host_no = sdev->host->host_no;
lli->in_table = false; lli->in_table = false;
...@@ -74,24 +73,19 @@ static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid) ...@@ -74,24 +73,19 @@ static struct glun_info *create_global(struct scsi_device *sdev, u8 *wwid)
} }
/** /**
* refresh_local() - find and update local LUN information structure by WWID * lookup_local() - find a local LUN information structure by WWID
* @cfg: Internal structure associated with the host. * @cfg: Internal structure associated with the host.
* @wwid: WWID associated with LUN. * @wwid: WWID associated with LUN.
* *
* When the LUN is found, mark it by updating it's newly_created field.
*
* Return: Found local lun_info structure on success, NULL on failure * Return: Found local lun_info structure on success, NULL on failure
* If a LUN with the WWID is found in the list, refresh it's state.
*/ */
static struct llun_info *refresh_local(struct cxlflash_cfg *cfg, u8 *wwid) static struct llun_info *lookup_local(struct cxlflash_cfg *cfg, u8 *wwid)
{ {
struct llun_info *lli, *temp; struct llun_info *lli, *temp;
list_for_each_entry_safe(lli, temp, &cfg->lluns, list) list_for_each_entry_safe(lli, temp, &cfg->lluns, list)
if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) { if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN))
lli->newly_created = false;
return lli; return lli;
}
return NULL; return NULL;
} }
...@@ -143,7 +137,7 @@ static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid) ...@@ -143,7 +137,7 @@ static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid)
if (unlikely(!wwid)) if (unlikely(!wwid))
goto out; goto out;
lli = refresh_local(cfg, wwid); lli = lookup_local(cfg, wwid);
if (lli) if (lli)
goto out; goto out;
...@@ -239,8 +233,8 @@ int cxlflash_manage_lun(struct scsi_device *sdev, ...@@ -239,8 +233,8 @@ int cxlflash_manage_lun(struct scsi_device *sdev,
mutex_lock(&global.mutex); mutex_lock(&global.mutex);
lli = find_and_create_lun(sdev, manage->wwid); lli = find_and_create_lun(sdev, manage->wwid);
pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n", pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n",
__func__, get_unaligned_le64(&manage->wwid[0]), __func__, get_unaligned_be64(&manage->wwid[0]),
get_unaligned_le64(&manage->wwid[8]), get_unaligned_be64(&manage->wwid[8]),
manage->hdr.flags, lli); manage->hdr.flags, lli);
if (unlikely(!lli)) { if (unlikely(!lli)) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -248,19 +242,25 @@ int cxlflash_manage_lun(struct scsi_device *sdev, ...@@ -248,19 +242,25 @@ int cxlflash_manage_lun(struct scsi_device *sdev,
} }
if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) { if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) {
if (lli->newly_created) /*
lli->port_sel = CHAN2PORT(chan); * Update port selection mask based upon channel, store off LUN
else * in unpacked, AFU-friendly format, and hang LUN reference in
lli->port_sel = BOTH_PORTS; * the sdev.
/* Store off lun in unpacked, AFU-friendly format */ */
lli->port_sel |= CHAN2PORT(chan);
lli->lun_id[chan] = lun_to_lunid(sdev->lun); lli->lun_id[chan] = lun_to_lunid(sdev->lun);
sdev->hostdata = lli; sdev->hostdata = lli;
} else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) { } else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) {
if (lli->parent->mode != MODE_NONE) if (lli->parent->mode != MODE_NONE)
rc = -EBUSY; rc = -EBUSY;
else else {
sdev->hostdata = NULL; sdev->hostdata = NULL;
lli->port_sel &= ~CHAN2PORT(chan);
} }
}
pr_debug("%s: port_sel = %08X chan = %u lun_id = %016llX\n", __func__,
lli->port_sel, chan, lli->lun_id[chan]);
out: out:
mutex_unlock(&global.mutex); mutex_unlock(&global.mutex);
......
...@@ -63,7 +63,6 @@ struct llun_info { ...@@ -63,7 +63,6 @@ struct llun_info {
u32 lun_index; /* Index in the LUN table */ u32 lun_index; /* Index in the LUN table */
u32 host_no; /* host_no from Scsi_host */ u32 host_no; /* host_no from Scsi_host */
u32 port_sel; /* What port to use for this LUN */ u32 port_sel; /* What port to use for this LUN */
bool newly_created; /* Whether the LUN was just discovered */
bool in_table; /* Whether a LUN table entry was created */ bool in_table; /* Whether a LUN table entry was created */
u8 wwid[16]; /* Keep a duplicate copy here? */ u8 wwid[16]; /* Keep a duplicate copy here? */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment