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

USB: EHCI: improve end_unlink_async()

This patch (as1665) changes the way ehci-hcd's end_unlink_async()
routine works in order to avoid recursive execution and to be more
efficient:

Now when an IAA cycle ends, a new one gets started up right
away (if it is needed) instead of waiting until the
just-unlinked QH has been processed.

The async_iaa list is renamed to async_idle, which better
expresses its new purpose: It is now the list of QHs which are
now completely idle and are waiting to be processed by
end_unlink_async().

A new flag is added to track whether an IAA cycle is in
progress, because the list formerly known as async_iaa no
longer stores the QHs waiting for the IAA to finish.

The decision about how many QHs to process when an IAA cycle
ends is now made at the end of the cycle, when we know the
current state of the hardware, rather than at the beginning.
This means a bunch of logic got moved from start_iaa_cycle()
to end_unlink_async().

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
214ac7a0 6e018751

+60 -46
+2 -2
drivers/usb/host/ehci-hcd.c
··· 483 483 */ 484 484 ehci->periodic_size = DEFAULT_I_TDPS; 485 485 INIT_LIST_HEAD(&ehci->async_unlink); 486 - INIT_LIST_HEAD(&ehci->async_iaa); 486 + INIT_LIST_HEAD(&ehci->async_idle); 487 487 INIT_LIST_HEAD(&ehci->intr_unlink); 488 488 INIT_LIST_HEAD(&ehci->intr_qh_list); 489 489 INIT_LIST_HEAD(&ehci->cached_itd_list); ··· 752 752 /* guard against (alleged) silicon errata */ 753 753 if (cmd & CMD_IAAD) 754 754 ehci_dbg(ehci, "IAA with IAAD still set?\n"); 755 - if (!list_empty(&ehci->async_iaa)) 755 + if (ehci->iaa_in_progress) 756 756 COUNT(ehci->stats.iaa); 757 757 end_unlink_async(ehci); 758 758 }
+55 -42
drivers/usb/host/ehci-q.c
··· 960 960 961 961 /* The async schedule and unlink lists are supposed to be empty */ 962 962 WARN_ON(ehci->async->qh_next.qh || !list_empty(&ehci->async_unlink) || 963 - !list_empty(&ehci->async_iaa)); 963 + !list_empty(&ehci->async_idle)); 964 964 965 965 /* Don't turn off the schedule until ASS is 1 */ 966 966 ehci_poll_ASS(ehci); ··· 1164 1164 ehci->qh_scan_next = qh->qh_next.qh; 1165 1165 } 1166 1166 1167 - static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) 1167 + static void start_iaa_cycle(struct ehci_hcd *ehci) 1168 1168 { 1169 - /* 1170 - * Do nothing if an IAA cycle is already running or 1171 - * if one will be started shortly. 1172 - */ 1173 - if (!list_empty(&ehci->async_iaa) || ehci->async_unlinking) 1169 + /* Do nothing if an IAA cycle is already running */ 1170 + if (ehci->iaa_in_progress) 1174 1171 return; 1172 + ehci->iaa_in_progress = true; 1175 1173 1176 1174 /* If the controller isn't running, we don't have to wait for it */ 1177 1175 if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { 1178 - 1179 - /* Do all the waiting QHs */ 1180 - list_splice_tail_init(&ehci->async_unlink, &ehci->async_iaa); 1181 - 1182 - if (!nested) /* Avoid recursion */ 1183 - end_unlink_async(ehci); 1176 + end_unlink_async(ehci); 1184 1177 1185 1178 /* Otherwise start a new IAA cycle */ 1186 1179 } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { 1187 - struct ehci_qh *qh; 1188 - 1189 - /* Do only the first waiting QH (nVidia bug?) */ 1190 - qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, 1191 - unlink_node); 1192 - 1193 - /* 1194 - * Intel (?) bug: The HC can write back the overlay region 1195 - * even after the IAA interrupt occurs. In self-defense, 1196 - * always go through two IAA cycles for each QH. 1197 - */ 1198 - if (qh->qh_state == QH_STATE_UNLINK_WAIT) 1199 - qh->qh_state = QH_STATE_UNLINK; 1200 - else 1201 - list_move_tail(&qh->unlink_node, &ehci->async_iaa); 1202 1180 1203 1181 /* Make sure the unlinks are all visible to the hardware */ 1204 1182 wmb(); ··· 1193 1215 static void end_unlink_async(struct ehci_hcd *ehci) 1194 1216 { 1195 1217 struct ehci_qh *qh; 1218 + bool early_exit; 1196 1219 1197 1220 if (ehci->has_synopsys_hc_bug) 1198 1221 ehci_writel(ehci, (u32) ehci->async->qh_dma, 1199 1222 &ehci->regs->async_next); 1200 1223 1224 + /* The current IAA cycle has ended */ 1225 + ehci->iaa_in_progress = false; 1226 + 1227 + if (list_empty(&ehci->async_unlink)) 1228 + return; 1229 + qh = list_first_entry(&ehci->async_unlink, struct ehci_qh, 1230 + unlink_node); /* QH whose IAA cycle just ended */ 1231 + 1232 + /* 1233 + * If async_unlinking is set then this routine is already running, 1234 + * either on the stack or on another CPU. 1235 + */ 1236 + early_exit = ehci->async_unlinking; 1237 + 1238 + /* If the controller isn't running, process all the waiting QHs */ 1239 + if (ehci->rh_state < EHCI_RH_RUNNING) 1240 + list_splice_tail_init(&ehci->async_unlink, &ehci->async_idle); 1241 + 1242 + /* 1243 + * Intel (?) bug: The HC can write back the overlay region even 1244 + * after the IAA interrupt occurs. In self-defense, always go 1245 + * through two IAA cycles for each QH. 1246 + */ 1247 + else if (qh->qh_state == QH_STATE_UNLINK_WAIT) { 1248 + qh->qh_state = QH_STATE_UNLINK; 1249 + early_exit = true; 1250 + } 1251 + 1252 + /* Otherwise process only the first waiting QH (NVIDIA bug?) */ 1253 + else 1254 + list_move_tail(&qh->unlink_node, &ehci->async_idle); 1255 + 1256 + /* Start a new IAA cycle if any QHs are waiting for it */ 1257 + if (!list_empty(&ehci->async_unlink)) 1258 + start_iaa_cycle(ehci); 1259 + 1260 + /* 1261 + * Don't allow nesting or concurrent calls, 1262 + * or wait for the second IAA cycle for the next QH. 1263 + */ 1264 + if (early_exit) 1265 + return; 1266 + 1201 1267 /* Process the idle QHs */ 1202 - restart: 1203 1268 ehci->async_unlinking = true; 1204 - while (!list_empty(&ehci->async_iaa)) { 1205 - qh = list_first_entry(&ehci->async_iaa, struct ehci_qh, 1269 + while (!list_empty(&ehci->async_idle)) { 1270 + qh = list_first_entry(&ehci->async_idle, struct ehci_qh, 1206 1271 unlink_node); 1207 1272 list_del(&qh->unlink_node); 1208 1273 ··· 1260 1239 disable_async(ehci); 1261 1240 } 1262 1241 ehci->async_unlinking = false; 1263 - 1264 - /* Start a new IAA cycle if any QHs are waiting for it */ 1265 - if (!list_empty(&ehci->async_unlink)) { 1266 - start_iaa_cycle(ehci, true); 1267 - if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) 1268 - goto restart; 1269 - } 1270 1242 } 1271 1243 1272 1244 static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); ··· 1284 1270 } 1285 1271 1286 1272 /* If nothing else is being unlinked, unlink the last empty QH */ 1287 - if (list_empty(&ehci->async_iaa) && list_empty(&ehci->async_unlink) && 1288 - qh_to_unlink) { 1273 + if (list_empty(&ehci->async_unlink) && qh_to_unlink) { 1289 1274 start_unlink_async(ehci, qh_to_unlink); 1290 1275 --count; 1291 1276 } ··· 1306 1293 WARN_ON(!list_empty(&qh->qtd_list)); 1307 1294 single_unlink_async(ehci, qh); 1308 1295 } 1309 - start_iaa_cycle(ehci, false); 1296 + start_iaa_cycle(ehci); 1310 1297 } 1311 1298 1312 1299 /* makes sure the async qh will become idle */ ··· 1319 1306 return; 1320 1307 1321 1308 single_unlink_async(ehci, qh); 1322 - start_iaa_cycle(ehci, false); 1309 + start_iaa_cycle(ehci); 1323 1310 } 1324 1311 1325 1312 /*-------------------------------------------------------------------------*/
+1 -1
drivers/usb/host/ehci-timer.c
··· 304 304 * (a) SMP races against real IAA firing and retriggering, and 305 305 * (b) clean HC shutdown, when IAA watchdog was pending. 306 306 */ 307 - if (ehci->rh_state != EHCI_RH_RUNNING) 307 + if (!ehci->iaa_in_progress || ehci->rh_state != EHCI_RH_RUNNING) 308 308 return; 309 309 310 310 /* If we get here, IAA is *REALLY* late. It's barely
+2 -1
drivers/usb/host/ehci.h
··· 121 121 bool scanning:1; 122 122 bool need_rescan:1; 123 123 bool intr_unlinking:1; 124 + bool iaa_in_progress:1; 124 125 bool async_unlinking:1; 125 126 bool shutdown:1; 126 127 struct ehci_qh *qh_scan_next; ··· 130 129 struct ehci_qh *async; 131 130 struct ehci_qh *dummy; /* For AMD quirk use */ 132 131 struct list_head async_unlink; 133 - struct list_head async_iaa; 132 + struct list_head async_idle; 134 133 unsigned async_unlink_cycle; 135 134 unsigned async_count; /* async activity count */ 136 135