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: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>
Acked-by: Manoj Kumar <manoj@linux.vnet.ibm.com>
Reviewed-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: James Bottomley <JBottomley@Odin.com>
This commit is contained in:
Matthew R. Ochs 2015-10-21 15:16:15 -05:00 committed by James Bottomley
parent e6e6df3f71
commit 1a47401bb3
2 changed files with 18 additions and 19 deletions

View File

@ -41,7 +41,6 @@ static struct llun_info *create_local(struct scsi_device *sdev, u8 *wwid)
}
lli->sdev = sdev;
lli->newly_created = true;
lli->host_no = sdev->host->host_no;
lli->in_table = false;
@ -74,24 +73,19 @@ out:
}
/**
* 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.
* @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
* 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;
list_for_each_entry_safe(lli, temp, &cfg->lluns, list)
if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN)) {
lli->newly_created = false;
if (!memcmp(lli->wwid, wwid, DK_CXLFLASH_MANAGE_LUN_WWID_LEN))
return lli;
}
return NULL;
}
@ -143,7 +137,7 @@ static struct llun_info *find_and_create_lun(struct scsi_device *sdev, u8 *wwid)
if (unlikely(!wwid))
goto out;
lli = refresh_local(cfg, wwid);
lli = lookup_local(cfg, wwid);
if (lli)
goto out;
@ -239,8 +233,8 @@ int cxlflash_manage_lun(struct scsi_device *sdev,
mutex_lock(&global.mutex);
lli = find_and_create_lun(sdev, manage->wwid);
pr_debug("%s: ENTER: WWID = %016llX%016llX, flags = %016llX li = %p\n",
__func__, get_unaligned_le64(&manage->wwid[0]),
get_unaligned_le64(&manage->wwid[8]),
__func__, get_unaligned_be64(&manage->wwid[0]),
get_unaligned_be64(&manage->wwid[8]),
manage->hdr.flags, lli);
if (unlikely(!lli)) {
rc = -ENOMEM;
@ -248,20 +242,26 @@ int cxlflash_manage_lun(struct scsi_device *sdev,
}
if (flags & DK_CXLFLASH_MANAGE_LUN_ENABLE_SUPERPIPE) {
if (lli->newly_created)
lli->port_sel = CHAN2PORT(chan);
else
lli->port_sel = BOTH_PORTS;
/* Store off lun in unpacked, AFU-friendly format */
/*
* Update port selection mask based upon channel, store off LUN
* in unpacked, AFU-friendly format, and hang LUN reference in
* the sdev.
*/
lli->port_sel |= CHAN2PORT(chan);
lli->lun_id[chan] = lun_to_lunid(sdev->lun);
sdev->hostdata = lli;
} else if (flags & DK_CXLFLASH_MANAGE_LUN_DISABLE_SUPERPIPE) {
if (lli->parent->mode != MODE_NONE)
rc = -EBUSY;
else
else {
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:
mutex_unlock(&global.mutex);
pr_debug("%s: returning rc=%d\n", __func__, rc);

View File

@ -63,7 +63,6 @@ struct llun_info {
u32 lun_index; /* Index in the LUN table */
u32 host_no; /* host_no from Scsi_host */
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 */
u8 wwid[16]; /* Keep a duplicate copy here? */