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

usb: gadget: f_tcm: Check overlapped command

If there's an overlapped command tag, cancel the command and respond
with RC_OVERLAPPED_TAG to host.

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/6bffc2903d0cd1e7c7afca837053a48e883d8903.1733876548.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Thinh Nguyen and committed by
Greg Kroah-Hartman
29ed1705 20e9ab60

+127 -1
+122 -1
drivers/usb/gadget/function/f_tcm.c
··· 686 686 687 687 case UASP_QUEUE_COMMAND: 688 688 /* 689 + * Overlapped command detected and cancelled. 690 + * So send overlapped attempted status. 691 + */ 692 + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG && 693 + req->status == -ECONNRESET) { 694 + uasp_send_tm_response(cmd); 695 + return; 696 + } 697 + 698 + hash_del(&stream->node); 699 + 700 + /* 689 701 * If no command submitted to target core here, just free the 690 702 * bitmap index. This is for the cases where f_tcm handles 691 703 * status response instead of the target core. 692 704 */ 693 - if (cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) { 705 + if (cmd->tmr_rsp != RC_OVERLAPPED_TAG && 706 + cmd->tmr_rsp != RC_RESPONSE_UNKNOWN) { 694 707 struct se_session *se_sess; 695 708 696 709 se_sess = fu->tpg->tpg_nexus->tvn_se_sess; ··· 715 702 } 716 703 717 704 usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC); 705 + complete(&stream->cmd_completion); 718 706 break; 719 707 720 708 default: ··· 724 710 return; 725 711 726 712 cleanup: 713 + hash_del(&stream->node); 727 714 transport_generic_free_cmd(&cmd->se_cmd, 0); 728 715 } 729 716 ··· 857 842 858 843 static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream) 859 844 { 845 + init_completion(&stream->cmd_completion); 846 + 860 847 stream->req_in = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL); 861 848 if (!stream->req_in) 862 849 goto out; ··· 1063 1046 cmd->state = UASP_QUEUE_COMMAND; 1064 1047 1065 1048 if (req->status == -ESHUTDOWN) { 1049 + struct uas_stream *stream = &cmd->fu->stream[se_cmd->map_tag]; 1050 + 1051 + hash_del(&stream->node); 1066 1052 target_put_sess_cmd(se_cmd); 1067 1053 transport_generic_free_cmd(&cmd->se_cmd, 0); 1068 1054 return; ··· 1089 1069 1090 1070 cleanup: 1091 1071 target_put_sess_cmd(se_cmd); 1072 + 1073 + /* Command was aborted due to overlapped tag */ 1074 + if (cmd->state == UASP_QUEUE_COMMAND && 1075 + cmd->tmr_rsp == RC_OVERLAPPED_TAG) { 1076 + uasp_send_tm_response(cmd); 1077 + return; 1078 + } 1079 + 1092 1080 transport_send_check_condition_and_sense(se_cmd, 1093 1081 TCM_CHECK_CONDITION_ABORT_CMD, 0); 1094 1082 } ··· 1164 1136 else 1165 1137 return uasp_send_read_response(cmd); 1166 1138 } 1139 + 1140 + static void usbg_aborted_task(struct se_cmd *se_cmd); 1167 1141 1168 1142 static void usbg_submit_tmr(struct usbg_cmd *cmd) 1169 1143 { ··· 1244 1214 return; 1245 1215 1246 1216 skip: 1217 + if (cmd->tmr_rsp == RC_OVERLAPPED_TAG) { 1218 + struct f_uas *fu = cmd->fu; 1219 + struct se_session *se_sess; 1220 + struct uas_stream *stream = NULL; 1221 + struct hlist_node *tmp; 1222 + struct usbg_cmd *active_cmd = NULL; 1223 + 1224 + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; 1225 + 1226 + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, cmd->tag) { 1227 + int i = stream - &fu->stream[0]; 1228 + 1229 + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; 1230 + if (active_cmd->tag == cmd->tag) 1231 + break; 1232 + } 1233 + 1234 + /* Sanity check */ 1235 + if (!stream || (active_cmd && active_cmd->tag != cmd->tag)) { 1236 + usbg_submit_command(cmd->fu, cmd->req); 1237 + return; 1238 + } 1239 + 1240 + reinit_completion(&stream->cmd_completion); 1241 + 1242 + /* 1243 + * A UASP command consists of the command, data, and status 1244 + * stages, each operating sequentially from different endpoints. 1245 + * 1246 + * Each USB endpoint operates independently, and depending on 1247 + * hardware implementation, a completion callback for a transfer 1248 + * from one endpoint may not reflect the order of completion on 1249 + * the wire. This is particularly true for devices with 1250 + * endpoints that have independent interrupts and event buffers. 1251 + * 1252 + * The driver must still detect misbehaving hosts and respond 1253 + * with an overlap status. To reduce false overlap failures, 1254 + * allow the active and matching stream ID a brief 1ms to 1255 + * complete before responding with an overlap command failure. 1256 + * Overlap failure should be rare. 1257 + */ 1258 + wait_for_completion_timeout(&stream->cmd_completion, msecs_to_jiffies(1)); 1259 + 1260 + /* If the previous stream is completed, retry the command. */ 1261 + if (!hash_hashed(&stream->node)) { 1262 + usbg_submit_command(cmd->fu, cmd->req); 1263 + return; 1264 + } 1265 + 1266 + /* 1267 + * The command isn't submitted to the target core, so we're safe 1268 + * to remove the bitmap index from the session tag pool. 1269 + */ 1270 + sbitmap_queue_clear(&se_sess->sess_tag_pool, 1271 + cmd->se_cmd.map_tag, 1272 + cmd->se_cmd.map_cpu); 1273 + 1274 + /* 1275 + * Overlap command tag detected. Cancel any pending transfer of 1276 + * the command submitted to target core. 1277 + */ 1278 + active_cmd->tmr_rsp = RC_OVERLAPPED_TAG; 1279 + usbg_aborted_task(&active_cmd->se_cmd); 1280 + 1281 + /* Send the response after the transfer is aborted. */ 1282 + return; 1283 + } 1284 + 1247 1285 uasp_send_tm_response(cmd); 1248 1286 } 1249 1287 ··· 1345 1247 struct usbg_cmd *cmd; 1346 1248 struct usbg_tpg *tpg = fu->tpg; 1347 1249 struct tcm_usbg_nexus *tv_nexus; 1250 + struct uas_stream *stream; 1251 + struct hlist_node *tmp; 1348 1252 struct command_iu *cmd_iu; 1349 1253 u32 cmd_len; 1350 1254 u16 scsi_tag; ··· 1382 1282 goto skip; 1383 1283 } 1384 1284 1285 + hash_for_each_possible_safe(fu->stream_hash, stream, tmp, node, scsi_tag) { 1286 + struct usbg_cmd *active_cmd; 1287 + struct se_session *se_sess; 1288 + int i = stream - &fu->stream[0]; 1289 + 1290 + se_sess = cmd->fu->tpg->tpg_nexus->tvn_se_sess; 1291 + active_cmd = &((struct usbg_cmd *)se_sess->sess_cmd_map)[i]; 1292 + 1293 + if (active_cmd->tag == scsi_tag) { 1294 + cmd->tmr_rsp = RC_OVERLAPPED_TAG; 1295 + goto skip; 1296 + } 1297 + } 1298 + 1299 + stream = &fu->stream[cmd->se_cmd.map_tag]; 1300 + hash_add(fu->stream_hash, &stream->node, scsi_tag); 1301 + 1385 1302 if (iu->iu_id == IU_ID_TASK_MGMT) { 1386 1303 struct task_mgmt_iu *tm_iu; 1387 1304 ··· 1410 1293 cmd_len = (cmd_iu->len & ~0x3) + 16; 1411 1294 if (cmd_len > USBG_MAX_CMD) { 1412 1295 target_free_tag(tv_nexus->tvn_se_sess, &cmd->se_cmd); 1296 + hash_del(&stream->node); 1413 1297 return -EINVAL; 1414 1298 } 1415 1299 memcpy(cmd->cmd_buf, cmd_iu->cdb, cmd_len); ··· 1561 1443 se_cmd); 1562 1444 struct se_session *se_sess = se_cmd->se_sess; 1563 1445 1446 + cmd->tag = 0; 1564 1447 kfree(cmd->data_buf); 1565 1448 target_free_tag(se_sess, se_cmd); 1566 1449 } ··· 2586 2467 fu->function.disable = tcm_disable; 2587 2468 fu->function.free_func = tcm_free; 2588 2469 fu->tpg = tpg_instances[i].tpg; 2470 + 2471 + hash_init(fu->stream_hash); 2589 2472 mutex_unlock(&tpg_instances_lock); 2590 2473 2591 2474 return &fu->function;
+5
drivers/usb/gadget/function/tcm.h
··· 4 4 5 5 #include <linux/kref.h> 6 6 /* #include <linux/usb/uas.h> */ 7 + #include <linux/hashtable.h> 7 8 #include <linux/usb/composite.h> 8 9 #include <linux/usb/uas.h> 9 10 #include <linux/usb/storage.h> ··· 104 103 struct usb_request *req_in; 105 104 struct usb_request *req_out; 106 105 struct usb_request *req_status; 106 + 107 + struct completion cmd_completion; 108 + struct hlist_node node; 107 109 }; 108 110 109 111 struct usbg_cdb { ··· 139 135 struct usb_ep *ep_status; 140 136 struct usb_ep *ep_cmd; 141 137 struct uas_stream stream[USBG_NUM_CMDS]; 138 + DECLARE_HASHTABLE(stream_hash, UASP_SS_EP_COMP_LOG_STREAMS); 142 139 143 140 /* BOT */ 144 141 struct bot_status bot_status;