Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 129776ac2e38 ("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

authored by

Youssef Samir and committed by
Jeff Hugo
2feec5ae 45ebe43e

+45 -2
+45 -2
drivers/accel/qaic/qaic_control.c
··· 914 914 */ 915 915 return -ENODEV; 916 916 917 - if (status) { 917 + if (usr && status) { 918 918 /* 919 919 * Releasing resources failed on the device side, which puts 920 920 * us in a bind since they may still be in use, so enable the ··· 1109 1109 mutex_lock(&qdev->cntl_mutex); 1110 1110 if (!list_empty(&elem.list)) 1111 1111 list_del(&elem.list); 1112 + /* resp_worker() processed the response but the wait was interrupted */ 1113 + else if (ret == -ERESTARTSYS) 1114 + ret = 0; 1112 1115 if (!ret && !elem.buf) 1113 1116 ret = -ETIMEDOUT; 1114 1117 else if (ret > 0 && !elem.buf) ··· 1422 1419 } 1423 1420 mutex_unlock(&qdev->cntl_mutex); 1424 1421 1425 - if (!found) 1422 + if (!found) { 1423 + /* 1424 + * The user might have gone away at this point without waiting 1425 + * for QAIC_TRANS_DEACTIVATE_FROM_DEV transaction coming from 1426 + * the device. If this is not handled correctly, the host will 1427 + * not know that the DBC[n] has been freed on the device. 1428 + * Due to this failure in synchronization between the device and 1429 + * the host, if another user requests to activate a network, and 1430 + * the device assigns DBC[n] again, save_dbc_buf() will hang, 1431 + * waiting for dbc[n]->in_use to be set to false, which will not 1432 + * happen unless the qaic_dev_reset_clean_local_state() gets 1433 + * called by resetting the device (or re-inserting the module). 1434 + * 1435 + * As a solution, we look for QAIC_TRANS_DEACTIVATE_FROM_DEV 1436 + * transactions in the message before disposing of it, then 1437 + * handle releasing the DBC resources. 1438 + * 1439 + * Since the user has gone away, if the device could not 1440 + * deactivate the network (status != 0), there is no way to 1441 + * enable and reassign the DBC to the user. We can put trust in 1442 + * the device that it will release all the active DBCs in 1443 + * response to the QAIC_TRANS_TERMINATE_TO_DEV transaction, 1444 + * otherwise, the user can issue an soc_reset to the device. 1445 + */ 1446 + u32 msg_count = le32_to_cpu(msg->hdr.count); 1447 + u32 msg_len = le32_to_cpu(msg->hdr.len); 1448 + u32 len = 0; 1449 + int j; 1450 + 1451 + for (j = 0; j < msg_count && len < msg_len; ++j) { 1452 + struct wire_trans_hdr *trans_hdr; 1453 + 1454 + trans_hdr = (struct wire_trans_hdr *)(msg->data + len); 1455 + if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) { 1456 + if (decode_deactivate(qdev, trans_hdr, &len, NULL)) 1457 + len += le32_to_cpu(trans_hdr->len); 1458 + } else { 1459 + len += le32_to_cpu(trans_hdr->len); 1460 + } 1461 + } 1426 1462 /* request must have timed out, drop packet */ 1427 1463 kfree(msg); 1464 + } 1428 1465 1429 1466 kfree(resp); 1430 1467 }