scsi: cxlflash: Avoid command room violation
During test, a command room violation interrupt is occasionally seen for the master context when the CXL flash devices are stressed. After studying the code, there could be gaps in the way command room value is being cached in cxlflash. When the cached command room is zero the thread attempting to send becomes burdened with updating the cached value with the actual value from the AFU. Today, this is handled with an atomic set operation of the raw value read. Following the atomic update, the thread proceeds to send. This behavior is incorrect on two counts: - The update fails to take into account the current thread and its consumption of one of the hardware commands. - The update does not take into account other threads also atomically updating. Per design, a worker thread updates the cached value when a send thread times out. By not protecting the update with a lock, the cached value can be incorrectly clobbered. To correct these issues, the update of the cached command room has been simplified and also protected using a spin lock which is held until the MMIO is complete. This ensures the command room is properly consumed by the same thread. Update of cached value also takes into account the current thread consuming a hardware command. Signed-off-by: Uma Krishnan <ukrishn@linux.vnet.ibm.com> Acked-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
3d2f617d44
commit
11f7b1844a
|
@ -173,8 +173,8 @@ struct afu {
|
|||
u64 *hrrq_end;
|
||||
u64 *hrrq_curr;
|
||||
bool toggle;
|
||||
bool read_room;
|
||||
atomic64_t room;
|
||||
s64 room;
|
||||
spinlock_t rrin_slock; /* Lock to rrin queuing and cmd_room updates */
|
||||
u64 hb;
|
||||
u32 cmd_couts; /* Number of command checkouts */
|
||||
u32 internal_lun; /* User-desired LUN mode for this AFU */
|
||||
|
|
|
@ -306,59 +306,34 @@ static int send_cmd(struct afu *afu, struct afu_cmd *cmd)
|
|||
{
|
||||
struct cxlflash_cfg *cfg = afu->parent;
|
||||
struct device *dev = &cfg->dev->dev;
|
||||
int nretry = 0;
|
||||
int rc = 0;
|
||||
u64 room;
|
||||
long newval;
|
||||
s64 room;
|
||||
ulong lock_flags;
|
||||
|
||||
/*
|
||||
* This routine is used by critical users such an AFU sync and to
|
||||
* send a task management function (TMF). Thus we want to retry a
|
||||
* bit before returning an error. To avoid the performance penalty
|
||||
* of MMIO, we spread the update of 'room' over multiple commands.
|
||||
* To avoid the performance penalty of MMIO, spread the update of
|
||||
* 'room' over multiple commands.
|
||||
*/
|
||||
retry:
|
||||
newval = atomic64_dec_if_positive(&afu->room);
|
||||
if (!newval) {
|
||||
do {
|
||||
spin_lock_irqsave(&afu->rrin_slock, lock_flags);
|
||||
if (--afu->room < 0) {
|
||||
room = readq_be(&afu->host_map->cmd_room);
|
||||
atomic64_set(&afu->room, room);
|
||||
if (room)
|
||||
goto write_ioarrin;
|
||||
udelay(1 << nretry);
|
||||
} while (nretry++ < MC_ROOM_RETRY_CNT);
|
||||
|
||||
dev_err(dev, "%s: no cmd_room to send 0x%X\n",
|
||||
__func__, cmd->rcb.cdb[0]);
|
||||
|
||||
goto no_room;
|
||||
} else if (unlikely(newval < 0)) {
|
||||
/* This should be rare. i.e. Only if two threads race and
|
||||
* decrement before the MMIO read is done. In this case
|
||||
* just benefit from the other thread having updated
|
||||
* afu->room.
|
||||
*/
|
||||
if (nretry++ < MC_ROOM_RETRY_CNT) {
|
||||
udelay(1 << nretry);
|
||||
goto retry;
|
||||
if (room <= 0) {
|
||||
dev_dbg_ratelimited(dev, "%s: no cmd_room to send "
|
||||
"0x%02X, room=0x%016llX\n",
|
||||
__func__, cmd->rcb.cdb[0], room);
|
||||
afu->room = 0;
|
||||
rc = SCSI_MLQUEUE_HOST_BUSY;
|
||||
goto out;
|
||||
}
|
||||
afu->room = room - 1;
|
||||
}
|
||||
|
||||
goto no_room;
|
||||
}
|
||||
|
||||
write_ioarrin:
|
||||
writeq_be((u64)&cmd->rcb, &afu->host_map->ioarrin);
|
||||
out:
|
||||
spin_unlock_irqrestore(&afu->rrin_slock, lock_flags);
|
||||
pr_devel("%s: cmd=%p len=%d ea=%p rc=%d\n", __func__, cmd,
|
||||
cmd->rcb.data_len, (void *)cmd->rcb.data_ea, rc);
|
||||
return rc;
|
||||
|
||||
no_room:
|
||||
afu->read_room = true;
|
||||
kref_get(&cfg->afu->mapcount);
|
||||
schedule_work(&cfg->work_q);
|
||||
rc = SCSI_MLQUEUE_HOST_BUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1827,7 +1802,8 @@ static int init_afu(struct cxlflash_cfg *cfg)
|
|||
}
|
||||
|
||||
afu_err_intr_init(cfg->afu);
|
||||
atomic64_set(&afu->room, readq_be(&afu->host_map->cmd_room));
|
||||
spin_lock_init(&afu->rrin_slock);
|
||||
afu->room = readq_be(&afu->host_map->cmd_room);
|
||||
|
||||
/* Restore the LUN mappings */
|
||||
cxlflash_restore_luntable(cfg);
|
||||
|
@ -2399,7 +2375,6 @@ MODULE_DEVICE_TABLE(pci, cxlflash_pci_table);
|
|||
* Handles the following events:
|
||||
* - Link reset which cannot be performed on interrupt context due to
|
||||
* blocking up to a few seconds
|
||||
* - Read AFU command room
|
||||
* - Rescan the host
|
||||
*/
|
||||
static void cxlflash_worker_thread(struct work_struct *work)
|
||||
|
@ -2436,11 +2411,6 @@ static void cxlflash_worker_thread(struct work_struct *work)
|
|||
cfg->lr_state = LINK_RESET_COMPLETE;
|
||||
}
|
||||
|
||||
if (afu->read_room) {
|
||||
atomic64_set(&afu->room, readq_be(&afu->host_map->cmd_room));
|
||||
afu->read_room = false;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(cfg->host->host_lock, lock_flags);
|
||||
|
||||
if (atomic_dec_if_positive(&cfg->scan_host_needed) >= 0)
|
||||
|
|
Loading…
Reference in New Issue