mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-16 00:51:51 -04:00
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: 129776ac2e ("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
This commit is contained in:
@@ -914,7 +914,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
|
||||
*/
|
||||
return -ENODEV;
|
||||
|
||||
if (status) {
|
||||
if (usr && status) {
|
||||
/*
|
||||
* Releasing resources failed on the device side, which puts
|
||||
* 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
|
||||
mutex_lock(&qdev->cntl_mutex);
|
||||
if (!list_empty(&elem.list))
|
||||
list_del(&elem.list);
|
||||
/* resp_worker() processed the response but the wait was interrupted */
|
||||
else if (ret == -ERESTARTSYS)
|
||||
ret = 0;
|
||||
if (!ret && !elem.buf)
|
||||
ret = -ETIMEDOUT;
|
||||
else if (ret > 0 && !elem.buf)
|
||||
@@ -1419,9 +1422,49 @@ static void resp_worker(struct work_struct *work)
|
||||
}
|
||||
mutex_unlock(&qdev->cntl_mutex);
|
||||
|
||||
if (!found)
|
||||
if (!found) {
|
||||
/*
|
||||
* The user might have gone away at this point without waiting
|
||||
* for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from
|
||||
* the device. If this is not handled correctly, the host will
|
||||
* not know that the DBC[n] has been freed on the device.
|
||||
* Due to this failure in synchronization between the device and
|
||||
* the host, if another user requests to activate a network, and
|
||||
* the device assigns DBC[n] again, save_dbc_buf() will hang,
|
||||
* waiting for dbc[n]->in_use to be set to false, which will not
|
||||
* happen unless the qaic_dev_reset_clean_local_state() gets
|
||||
* called by resetting the device (or re-inserting the module).
|
||||
*
|
||||
* As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV
|
||||
* transactions in the message before disposing of it, then
|
||||
* handle releasing the DBC resources.
|
||||
*
|
||||
* Since the user has gone away, if the device could not
|
||||
* deactivate the network (status != 0), there is no way to
|
||||
* enable and reassign the DBC to the user. We can put trust in
|
||||
* the device that it will release all the active DBCs in
|
||||
* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
|
||||
* otherwise, the user can issue an soc_reset to the device.
|
||||
*/
|
||||
u32 msg_count = le32_to_cpu(msg->hdr.count);
|
||||
u32 msg_len = le32_to_cpu(msg->hdr.len);
|
||||
u32 len = 0;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < msg_count && len < msg_len; ++j) {
|
||||
struct wire_trans_hdr *trans_hdr;
|
||||
|
||||
trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
|
||||
if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
|
||||
if (decode_deactivate(qdev, trans_hdr, &len, NULL))
|
||||
len += le32_to_cpu(trans_hdr->len);
|
||||
} else {
|
||||
len += le32_to_cpu(trans_hdr->len);
|
||||
}
|
||||
}
|
||||
/* request must have timed out, drop packet */
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
kfree(resp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user