Skip to content

Commit e589a51

Browse files
arekinathdanmcd
authored andcommitted
14677 mlxcx NULL deref panic due to race in mlxcx_cmd_taskq
Change-Id: If82cbd13b21fac25c929afa096e0bdb53c26e46d
1 parent f73a069 commit e589a51

File tree

3 files changed

+59
-37
lines changed

3 files changed

+59
-37
lines changed

usr/src/uts/common/io/mlxcx/mlxcx.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -315,12 +315,7 @@ typedef struct mlxcx_cmd_queue {
315315
uint8_t mcmd_size_l2;
316316
uint8_t mcmd_stride_l2;
317317
uint_t mcmd_size;
318-
/*
319-
* The mask has a bit for each command slot, there are a maximum
320-
* of 32 slots. When the bit is set in the mask, it indicates
321-
* the slot is available.
322-
*/
323-
uint32_t mcmd_mask;
318+
uint8_t mcmd_next; /* next command slot */
324319

325320
mlxcx_cmd_t *mcmd_active[MLXCX_CMD_MAX];
326321

usr/src/uts/common/io/mlxcx/mlxcx_cmd.c

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ mlxcx_cmd_queue_init(mlxcx_t *mlxp)
569569
return (B_FALSE);
570570
}
571571

572-
cmd->mcmd_mask = (uint32_t)((1ULL << cmd->mcmd_size) - 1);
572+
cmd->mcmd_next = 0;
573573

574574
mutex_init(&cmd->mcmd_lock, NULL, MUTEX_DRIVER, NULL);
575575
cv_init(&cmd->mcmd_cv, NULL, CV_DRIVER, NULL);
@@ -840,31 +840,34 @@ mlxcx_cmd_copy_output(mlxcx_cmd_ent_t *ent, mlxcx_cmd_t *cmd)
840840
}
841841

842842
static uint_t
843-
mlxcx_cmd_reserve_slot(mlxcx_cmd_queue_t *cmdq)
843+
mlxcx_cmd_reserve_slot(mlxcx_cmd_queue_t *cmdq, mlxcx_cmd_t *cmd)
844844
{
845-
uint_t slot;
846-
845+
uint_t i, slot;
846+
ASSERT(mutex_owned(&cmd->mlcmd_lock));
847847
mutex_enter(&cmdq->mcmd_lock);
848-
slot = ddi_ffs(cmdq->mcmd_mask);
849-
while (slot == 0) {
848+
while (1) {
849+
for (i = 0; i < MLXCX_CMD_MAX; ++i) {
850+
slot = (cmdq->mcmd_next + i) % MLXCX_CMD_MAX;
851+
if (cmdq->mcmd_active[slot] == NULL)
852+
break;
853+
}
854+
if (cmdq->mcmd_active[slot] == NULL) {
855+
cmdq->mcmd_active[slot] = cmd;
856+
cmdq->mcmd_next = slot + 1;
857+
mutex_exit(&cmdq->mcmd_lock);
858+
return (slot);
859+
}
850860
cv_wait(&cmdq->mcmd_cv, &cmdq->mcmd_lock);
851-
slot = ddi_ffs(cmdq->mcmd_mask);
852861
}
853-
854-
cmdq->mcmd_mask &= ~(1U << --slot);
855-
856-
ASSERT3P(cmdq->mcmd_active[slot], ==, NULL);
857-
858-
mutex_exit(&cmdq->mcmd_lock);
859-
860-
return (slot);
861862
}
862863

863864
static void
864-
mlxcx_cmd_release_slot(mlxcx_cmd_queue_t *cmdq, uint_t slot)
865+
mlxcx_cmd_release_slot(mlxcx_cmd_queue_t *cmdq, uint_t slot, mlxcx_cmd_t *cmd)
865866
{
867+
ASSERT(mutex_owned(&cmd->mlcmd_lock));
866868
mutex_enter(&cmdq->mcmd_lock);
867-
cmdq->mcmd_mask |= 1U << slot;
869+
ASSERT3P(cmdq->mcmd_active[slot], ==, cmd);
870+
cmdq->mcmd_active[slot] = NULL;
868871
cv_broadcast(&cmdq->mcmd_cv);
869872
mutex_exit(&cmdq->mcmd_lock);
870873
}
@@ -876,6 +879,8 @@ mlxcx_cmd_done(mlxcx_cmd_t *cmd, uint_t slot)
876879
mlxcx_cmd_queue_t *cmdq = &mlxp->mlx_cmd;
877880
mlxcx_cmd_ent_t *ent;
878881

882+
ASSERT(mutex_owned(&cmd->mlcmd_lock));
883+
879884
/*
880885
* Command is done. Save relevant data. Once we broadcast on the CV and
881886
* drop the lock, we must not touch it again.
@@ -885,17 +890,16 @@ mlxcx_cmd_done(mlxcx_cmd_t *cmd, uint_t slot)
885890
ent = (mlxcx_cmd_ent_t *)(cmdq->mcmd_dma.mxdb_va +
886891
(slot << cmdq->mcmd_stride_l2));
887892

888-
mutex_enter(&cmd->mlcmd_lock);
889893
cmd->mlcmd_status = MLXCX_CMD_STATUS(ent->mce_status);
890894
if (cmd->mlcmd_status == 0)
891895
mlxcx_cmd_copy_output(ent, cmd);
892896

893897
cmd->mlcmd_state = MLXCX_CMD_S_DONE;
894898
cv_broadcast(&cmd->mlcmd_cv);
895-
mutex_exit(&cmd->mlcmd_lock);
896899

897-
cmdq->mcmd_active[slot] = NULL;
898-
mlxcx_cmd_release_slot(cmdq, slot);
900+
mlxcx_cmd_release_slot(cmdq, slot, cmd);
901+
902+
mutex_exit(&cmd->mlcmd_lock);
899903
}
900904

901905
static void
@@ -907,14 +911,14 @@ mlxcx_cmd_taskq(void *arg)
907911
mlxcx_cmd_ent_t *ent;
908912
uint_t poll, slot;
909913

910-
ASSERT3S(cmd->mlcmd_op, !=, 0);
914+
mutex_enter(&cmd->mlcmd_lock);
915+
916+
VERIFY3S(cmd->mlcmd_op, !=, 0);
911917

912-
slot = mlxcx_cmd_reserve_slot(cmdq);
918+
slot = mlxcx_cmd_reserve_slot(cmdq, cmd);
913919
ent = (mlxcx_cmd_ent_t *)(cmdq->mcmd_dma.mxdb_va +
914920
(slot << cmdq->mcmd_stride_l2));
915921

916-
cmdq->mcmd_active[slot] = cmd;
917-
918922
/*
919923
* Command queue is currently ours as we set busy.
920924
*/
@@ -924,15 +928,25 @@ mlxcx_cmd_taskq(void *arg)
924928
ent->mce_out_length = to_be32(cmd->mlcmd_outlen);
925929
ent->mce_token = cmd->mlcmd_token;
926930
ent->mce_sig = 0;
927-
ent->mce_status = MLXCX_CMD_HW_OWNED;
928931
mlxcx_cmd_prep_input(ent, cmd);
929932
mlxcx_cmd_prep_output(ent, cmd);
933+
934+
/*
935+
* Ensure all of the other fields of the entry are written before
936+
* we switch the owner to hardware (the device might start executing
937+
* right away)
938+
*/
939+
membar_producer();
940+
ent->mce_status = MLXCX_CMD_HW_OWNED;
941+
930942
MLXCX_DMA_SYNC(cmdq->mcmd_dma, DDI_DMA_SYNC_FORDEV);
931943

932944
mlxcx_put32(mlxp, MLXCX_ISS_CMD_DOORBELL, 1 << slot);
933945

934-
if (!cmd->mlcmd_poll)
946+
if (!cmd->mlcmd_poll) {
947+
mutex_exit(&cmd->mlcmd_lock);
935948
return;
949+
}
936950

937951
for (poll = 0; poll < mlxcx_cmd_tries; poll++) {
938952
delay(drv_usectohz(mlxcx_cmd_delay));
@@ -947,21 +961,21 @@ mlxcx_cmd_taskq(void *arg)
947961
*/
948962

949963
if (poll == mlxcx_cmd_tries) {
950-
mutex_enter(&cmd->mlcmd_lock);
951964
cmd->mlcmd_status = MLXCX_CMD_R_TIMEOUT;
952965
cmd->mlcmd_state = MLXCX_CMD_S_ERROR;
953966
cv_broadcast(&cmd->mlcmd_cv);
967+
968+
mlxcx_cmd_release_slot(cmdq, slot, cmd);
969+
954970
mutex_exit(&cmd->mlcmd_lock);
955971

956972
mlxcx_fm_ereport(mlxp, DDI_FM_DEVICE_NO_RESPONSE);
957973

958-
cmdq->mcmd_active[slot] = NULL;
959-
mlxcx_cmd_release_slot(cmdq, slot);
960-
961974
return;
962975
}
963976

964977
mlxcx_cmd_done(cmd, slot);
978+
/* mlxcx_cmd_done releases mlcmd_lock */
965979
}
966980

967981
void
@@ -980,10 +994,17 @@ mlxcx_cmd_completion(mlxcx_t *mlxp, mlxcx_eventq_ent_t *ent)
980994
comp_vec &= ~(1U << --slot);
981995

982996
cmd = cmdq->mcmd_active[slot];
997+
998+
/*
999+
* This field is never modified, so we shouldn't need to hold
1000+
* mlcmd_lock before checking it.
1001+
*/
9831002
if (cmd->mlcmd_poll)
9841003
continue;
9851004

1005+
mutex_enter(&cmd->mlcmd_lock);
9861006
mlxcx_cmd_done(cmd, slot);
1007+
/* mlxcx_cmd_done releases mlcmd_lock */
9871008
}
9881009
}
9891010

usr/src/uts/common/io/mlxcx/mlxcx_ring.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,12 @@ mlxcx_sq_ring_dbell(mlxcx_t *mlxp, mlxcx_work_queue_t *mlwq, uint_t first)
14531453
ASSERT3U(mlwq->mlwq_type, ==, MLXCX_WQ_TYPE_SENDQ);
14541454
ASSERT(mutex_owned(&mlwq->mlwq_mtx));
14551455

1456+
/*
1457+
* Make sure all prior stores are flushed out before we update the
1458+
* counter: hardware can immediately start executing after this write
1459+
* (the doorbell below just makes sure it's awake)
1460+
*/
1461+
membar_producer();
14561462
mlwq->mlwq_doorbell->mlwqd_send_counter = to_be16(mlwq->mlwq_pc);
14571463

14581464
ASSERT(mlwq->mlwq_cq != NULL);

0 commit comments

Comments
 (0)