Skip to content

Commit e997c21

Browse files
committed
accel/qaic: Fix NNC message corruption
If msg_xfer() is unable to queue part of a NNC message because the MHI ring is full, it will attempt to give the QSM some time to drain the queue. However, if QSM fails to make any room, msg_xfer() will fail and tell the caller to try again. This is problematic because part of the message may have been committed to the ring and there is no mechanism to revoke that content. This will cause QSM to receive a corrupt message. The better way to do this is to check if the ring has enough space for the entire message before committing any of the message. Since msg_xfer() is under the cntl_mutex no one else can come in and consume the space. Fixes: 129776a ("accel/qaic: Add control path") Signed-off-by: Jeffrey Hugo <[email protected]> Reviewed-by: Pranjal Ramajor Asha Kanojiya <[email protected]> Reviewed-by: Carl Vanderlip <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 75af0a5 commit e997c21

File tree

1 file changed

+21
-8
lines changed

1 file changed

+21
-8
lines changed

drivers/accel/qaic/qaic_control.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -997,14 +997,34 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
997997
struct xfer_queue_elem elem;
998998
struct wire_msg *out_buf;
999999
struct wrapper_msg *w;
1000+
long ret = -EAGAIN;
1001+
int xfer_count = 0;
10001002
int retry_count;
1001-
long ret;
10021003

10031004
if (qdev->in_reset) {
10041005
mutex_unlock(&qdev->cntl_mutex);
10051006
return ERR_PTR(-ENODEV);
10061007
}
10071008

1009+
/* Attempt to avoid a partial commit of a message */
1010+
list_for_each_entry(w, &wrappers->list, list)
1011+
xfer_count++;
1012+
1013+
for (retry_count = 0; retry_count < QAIC_MHI_RETRY_MAX; retry_count++) {
1014+
if (xfer_count <= mhi_get_free_desc_count(qdev->cntl_ch, DMA_TO_DEVICE)) {
1015+
ret = 0;
1016+
break;
1017+
}
1018+
msleep_interruptible(QAIC_MHI_RETRY_WAIT_MS);
1019+
if (signal_pending(current))
1020+
break;
1021+
}
1022+
1023+
if (ret) {
1024+
mutex_unlock(&qdev->cntl_mutex);
1025+
return ERR_PTR(ret);
1026+
}
1027+
10081028
elem.seq_num = seq_num;
10091029
elem.buf = NULL;
10101030
init_completion(&elem.xfer_done);
@@ -1038,16 +1058,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
10381058
list_for_each_entry(w, &wrappers->list, list) {
10391059
kref_get(&w->ref_count);
10401060
retry_count = 0;
1041-
retry:
10421061
ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len,
10431062
list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN);
10441063
if (ret) {
1045-
if (ret == -EAGAIN && retry_count++ < QAIC_MHI_RETRY_MAX) {
1046-
msleep_interruptible(QAIC_MHI_RETRY_WAIT_MS);
1047-
if (!signal_pending(current))
1048-
goto retry;
1049-
}
1050-
10511064
qdev->cntl_lost_buf = true;
10521065
kref_put(&w->ref_count, free_wrapper);
10531066
mutex_unlock(&qdev->cntl_mutex);

0 commit comments

Comments
 (0)