Skip to content

Commit 2feec5a

Browse files
Youssef Samirquic-jhugo
authored andcommitted
accel/qaic: Handle DBC deactivation if the owner went away
When a DBC is released, the device sends a QAIC_TRANS_DEACTIVATE_FROM_DEV transaction to the host over the QAIC_CONTROL MHI channel. QAIC handles this by calling decode_deactivate() to release the resources allocated for that DBC. Since that handling is done in the qaic_manage_ioctl() context, if the user goes away before receiving and handling the deactivation, the host will be out-of-sync with the DBCs available for use, and the DBC resources will not be freed unless the device is removed. If another user loads and requests to activate a network, then the device assigns the same DBC to that network, QAIC will "indefinitely" wait for dbc->in_use = false, leading the user process to hang. As a solution to this, handle QAIC_TRANS_DEACTIVATE_FROM_DEV transactions that are received after the user has gone away. Fixes: 129776a ("accel/qaic: Add control path") Signed-off-by: Youssef Samir <youssef.abdulrahman@oss.qualcomm.com> Reviewed-by: Lizhi Hou <lizhi.hou@amd.com> Reviewed-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Signed-off-by: Jeff Hugo <jeff.hugo@oss.qualcomm.com> Link: https://patch.msgid.link/20260205123415.3870898-1-youssef.abdulrahman@oss.qualcomm.com
1 parent 45ebe43 commit 2feec5a

1 file changed

Lines changed: 45 additions & 2 deletions

File tree

drivers/accel/qaic/qaic_control.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
914914
*/
915915
return -ENODEV;
916916

917-
if (status) {
917+
if (usr && status) {
918918
/*
919919
* Releasing resources failed on the device side, which puts
920920
* us in a bind since they may still be in use, so enable the
@@ -1109,6 +1109,9 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
11091109
mutex_lock(&qdev->cntl_mutex);
11101110
if (!list_empty(&elem.list))
11111111
list_del(&elem.list);
1112+
/* resp_worker() processed the response but the wait was interrupted */
1113+
else if (ret == -ERESTARTSYS)
1114+
ret = 0;
11121115
if (!ret && !elem.buf)
11131116
ret = -ETIMEDOUT;
11141117
else if (ret > 0 && !elem.buf)
@@ -1419,9 +1422,49 @@ static void resp_worker(struct work_struct *work)
14191422
}
14201423
mutex_unlock(&qdev->cntl_mutex);
14211424

1422-
if (!found)
1425+
if (!found) {
1426+
/*
1427+
* The user might have gone away at this point without waiting
1428+
* for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from
1429+
* the device. If this is not handled correctly, the host will
1430+
* not know that the DBC[n] has been freed on the device.
1431+
* Due to this failure in synchronization between the device and
1432+
* the host, if another user requests to activate a network, and
1433+
* the device assigns DBC[n] again, save_dbc_buf() will hang,
1434+
* waiting for dbc[n]->in_use to be set to false, which will not
1435+
* happen unless the qaic_dev_reset_clean_local_state() gets
1436+
* called by resetting the device (or re-inserting the module).
1437+
*
1438+
* As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV
1439+
* transactions in the message before disposing of it, then
1440+
* handle releasing the DBC resources.
1441+
*
1442+
* Since the user has gone away, if the device could not
1443+
* deactivate the network (status != 0), there is no way to
1444+
* enable and reassign the DBC to the user. We can put trust in
1445+
* the device that it will release all the active DBCs in
1446+
* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
1447+
* otherwise, the user can issue an soc_reset to the device.
1448+
*/
1449+
u32 msg_count = le32_to_cpu(msg->hdr.count);
1450+
u32 msg_len = le32_to_cpu(msg->hdr.len);
1451+
u32 len = 0;
1452+
int j;
1453+
1454+
for (j = 0; j < msg_count && len < msg_len; ++j) {
1455+
struct wire_trans_hdr *trans_hdr;
1456+
1457+
trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
1458+
if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
1459+
if (decode_deactivate(qdev, trans_hdr, &len, NULL))
1460+
len += le32_to_cpu(trans_hdr->len);
1461+
} else {
1462+
len += le32_to_cpu(trans_hdr->len);
1463+
}
1464+
}
14231465
/* request must have timed out, drop packet */
14241466
kfree(msg);
1467+
}
14251468

14261469
kfree(resp);
14271470
}

0 commit comments

Comments
 (0)