Commit 7f3fdef
comms/uniflow/tcp: fail loudly on the paths that quietly did the wrong thing (meta-pytorch#3921)
Summary:
Folded from two adjacent diffs; each half is stated separately below so the two
arguments stay reviewable on their own terms.
--- outbound: a failed operation must not reach the peer (was D117927060) ----
One invariant, enforced from the two ends it was missing: the peer must not do work for
an operation whose caller has already been told it failed. put() has held that line since
its pre-flight/commit split; these are the two places that did not. Folded together
because they are halves of the same argument and each is small -- the second is the one
that made the first insufficient.
--- Before the first frame is queued (was D117888775) --------------------------
get() looked up each request's remote handle in the loop that also admits and
queues frames, so a multi-request get() whose second request carries no TCP handle
failed after the first request's ReadRequests were already queued. The peer serviced
reads for an operation the caller had already been told had failed.
put() settles everything that can fail before its first frame is queued and says so
at the top of its pre-flight loop. get() did not hold that line. This moves the
lookup into the loop that already walks requests to count chunks, and carries the
resolved segIds forward, so a rejected get() leaves the peer untouched.
Not a correctness fix in the load-bearing sense and not a performance one. The caller
already saw the same InvalidArgument, and the destination buffer was never at risk:
fail() marks the op done, so a late ReadReply fails tryBeginWrite() and skips the
copy rather than writing into a buffer the caller may have released. What changed is
that the peer no longer does work for a rejected operation, and those inflight_ slots
are no longer held for a round trip.
No hot-path effect either way: findRemoteHandle() was already called once per request
rather than once per chunk, so this reorders the same work instead of removing any.
Benchmarked regardless, because the get path is measured rather than argued about.
--- After teardown has swept (was D117927060) ----------------------------------
The three admission points disagreed about connBroken_. admitInflight() and
recvImpl() re-test it under their container mutex, because the caller's entry check is
not enough on its own -- failAllPending() can land in the gap between that check and
the insert. The three enqueue paths listed connBroken_ only in their wait predicate, as
a wake condition, and after waking checked outClosed alone.
failAllPending() sets connBroken_ and clears every lane queue, but never sets outClosed:
the only writer is a sender that has died. handleFrame's exception containment sweeps
without closing the connection on purpose, so the reachable state is connBroken_ set,
reader stopped, connection open, sender alive indefinitely. A frame admitted just before
that sweep then lands in the just-cleared queue and the live sender transmits it.
For put and get that means a Write reaching the peer's segment for an operation whose
caller has already been resolved with ConnectionFailed -- a partial write at offsets
nobody is told about, which is the case put()'s pre-flight/commit split exists to
prevent. For send it means the promise completes successfully on a transport that has
failed everything else and stopped reading.
Now all three check connBroken_ alongside outClosed and route it down the path they
already had for refusal: enqueueFrame/enqueueFrames report false so the caller fails the
op, and enqueueSendFrame takes ownership of the promise and fails it.
senderLoop's own outClosed check is deliberately left alone. It has to keep draining
whatever is already queued; refusing there would abandon frames rather than admit them.
--- inbound: a mismatched reply must not complete an operation (was D117932250) ---
TcpInflight::isRead records whether a chunk came from get() or put(), and the
reply handler only consulted it on the ReadReply branch. The Ack branch did not, so an
Ack naming a get chunk fell through to completeOne(): the chunk resolved Ok with
entry.dst never written, and the caller read back whatever its destination buffer
already held.
That is the one peer-supplied dimension on this path that failed silently. segId,
offset, len and payload size are all checked and all produce an error; a crossed reply
kind produced success with wrong data.
Both directions are now settled in one place, immediately after the entry lookup, and
the check is exhaustive over the three ops that reach it: an Ack must name a write and
a ReadReply must name a read. Error stays exempt because it is kind-agnostic by design.
Consolidating also fixes a diagnostic that was wrong before: !entry.isRead used to
report "read reply size mismatch", so a skewed peer sending a ReadReply for a put chunk
sent the reader chasing a length bug that did not exist. That branch is now purely a
size check and its message is accurate.
Not reachable from a same-version peer -- Ack answers a Write, ReadReply answers a
ReadRequest, and reqIds are unique per chunk -- so this is version skew or a hostile
peer, the same bar as the oversized-ReadRequest check.
Reviewed By: rmahidhar
Differential Revision: D1179270601 parent 74f6517 commit 7f3fdef
2 files changed
Lines changed: 162 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
988 | 988 | | |
989 | 989 | | |
990 | 990 | | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
991 | 995 | | |
| 996 | + | |
| 997 | + | |
992 | 998 | | |
993 | 999 | | |
994 | 1000 | | |
995 | 1001 | | |
996 | 1002 | | |
997 | 1003 | | |
998 | 1004 | | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
999 | 1011 | | |
1000 | 1012 | | |
1001 | 1013 | | |
| |||
1013 | 1025 | | |
1014 | 1026 | | |
1015 | 1027 | | |
1016 | | - | |
1017 | | - | |
1018 | | - | |
1019 | | - | |
1020 | | - | |
1021 | | - | |
1022 | | - | |
| 1028 | + | |
| 1029 | + | |
| 1030 | + | |
1023 | 1031 | | |
1024 | 1032 | | |
1025 | 1033 | | |
| |||
1834 | 1842 | | |
1835 | 1843 | | |
1836 | 1844 | | |
1837 | | - | |
| 1845 | + | |
| 1846 | + | |
| 1847 | + | |
| 1848 | + | |
| 1849 | + | |
| 1850 | + | |
| 1851 | + | |
| 1852 | + | |
| 1853 | + | |
1838 | 1854 | | |
1839 | 1855 | | |
1840 | 1856 | | |
| |||
1871 | 1887 | | |
1872 | 1888 | | |
1873 | 1889 | | |
1874 | | - | |
| 1890 | + | |
1875 | 1891 | | |
1876 | 1892 | | |
1877 | 1893 | | |
| |||
1913 | 1929 | | |
1914 | 1930 | | |
1915 | 1931 | | |
1916 | | - | |
| 1932 | + | |
1917 | 1933 | | |
1918 | 1934 | | |
1919 | 1935 | | |
| |||
2354 | 2370 | | |
2355 | 2371 | | |
2356 | 2372 | | |
| 2373 | + | |
| 2374 | + | |
| 2375 | + | |
| 2376 | + | |
| 2377 | + | |
| 2378 | + | |
| 2379 | + | |
| 2380 | + | |
| 2381 | + | |
| 2382 | + | |
| 2383 | + | |
| 2384 | + | |
| 2385 | + | |
| 2386 | + | |
| 2387 | + | |
2357 | 2388 | | |
2358 | 2389 | | |
2359 | 2390 | | |
2360 | 2391 | | |
2361 | 2392 | | |
2362 | | - | |
2363 | | - | |
| 2393 | + | |
| 2394 | + | |
| 2395 | + | |
2364 | 2396 | | |
2365 | 2397 | | |
2366 | 2398 | | |
| |||
Lines changed: 118 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| 209 | + | |
| 210 | + | |
209 | 211 | | |
210 | 212 | | |
211 | 213 | | |
| |||
1179 | 1181 | | |
1180 | 1182 | | |
1181 | 1183 | | |
| 1184 | + | |
| 1185 | + | |
| 1186 | + | |
| 1187 | + | |
| 1188 | + | |
| 1189 | + | |
| 1190 | + | |
| 1191 | + | |
| 1192 | + | |
| 1193 | + | |
| 1194 | + | |
| 1195 | + | |
| 1196 | + | |
| 1197 | + | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
| 1257 | + | |
| 1258 | + | |
| 1259 | + | |
| 1260 | + | |
| 1261 | + | |
| 1262 | + | |
| 1263 | + | |
| 1264 | + | |
| 1265 | + | |
| 1266 | + | |
| 1267 | + | |
| 1268 | + | |
| 1269 | + | |
| 1270 | + | |
| 1271 | + | |
| 1272 | + | |
| 1273 | + | |
| 1274 | + | |
| 1275 | + | |
| 1276 | + | |
| 1277 | + | |
| 1278 | + | |
| 1279 | + | |
| 1280 | + | |
| 1281 | + | |
| 1282 | + | |
| 1283 | + | |
| 1284 | + | |
| 1285 | + | |
| 1286 | + | |
| 1287 | + | |
| 1288 | + | |
| 1289 | + | |
| 1290 | + | |
| 1291 | + | |
| 1292 | + | |
| 1293 | + | |
| 1294 | + | |
| 1295 | + | |
| 1296 | + | |
| 1297 | + | |
| 1298 | + | |
| 1299 | + | |
1182 | 1300 | | |
1183 | 1301 | | |
1184 | 1302 | | |
| |||
0 commit comments