network: multi-record DaynaPort READ(6) matching real SCSI/Link behavior - #399
network: multi-record DaynaPort READ(6) matching real SCSI/Link behavior#399ingpaschke wants to merge 4 commits into
Conversation
scsiNetworkEnqueue advanced writeIndex onto readIndex when the ring filled, which makes a completely full ring indistinguishable from an empty one: the DaynaPort READ(6) handler then reports no packets and every queued packet is dropped at once, showing up as bursts of lost inbound frames and TCP retransmissions under load. Compute the next write index first and drop only the incoming packet when the ring is full.
The 20-slot ring overflows during wifi receive bursts between host READ(6) polls, dropping packets that then cost TCP retransmissions. The Pico 2 W target has the RAM for a deeper ring (~1.5 KB/slot), so set NETWORK_PACKET_QUEUE_SIZE=48 in its target definitions, using the existing override hook (as Pico_Audio_SPDIF already does with 12). Other targets keep the 20-slot default.
|
A bit outside the scope of this PR, and to be clear the gate isn't mine: 7c20579 came in via #350, which is why there was never a thread for it. On the substance: CDB[5] is just how the driver tells the device how it intends to clock bytes off the bus. Why it picks one mode over the other is a host-side decision, and I don't think the firmware should be modelling it. Respecting the request is the right call. This new code does multiple packets per read in polled mode anyway (4 records rather than 1, roughly the real SCSI/Link's 6 KB packet memory) so nothing collapses to a single packet either way. An interesting aside: So your pause timings, which I left untouched, were captured from a real SCSI/Link-3 with a Mac Plus as the host. If the Plus is requesting polled mode, then those numbers characterise polled-mode pacing and we're applying them unchanged to blind hosts, where the requirement is real but the magnitude is unvalidated. I've only tested them on/off: removing them breaks blind-mode on my SE/30 and is worth about 10 KB/s on the Atari Falcon. I never swept the value. Do you know what CDB[5] your Plus actually sends, and did the SCSI/Link pace differently depending on that bit? Could you dump the ROM of the real Dayna SCSI/Link? I just tested Appletalk performance on my SE/30 with VM on on 7.5.5 and it's totally fine with the new code at least. |
|
I found the ROMs for the Dayna SCSI/Link! Will reverse engineer and that will give me an even better understanding what the real hardware did. |
Derived from Dayna SCSI/Link SL003 device firmware (Z180 ROMs v1.3b2 and v2.0; addresses from v2.0). CDB[5] bit 6 selects the host's transfer loop (blind/polled); the ROM dispatches on it at 0x0f3b. - Polled: one record per READ(6), no terminator (ROM 0x0d77). DaynaPORT 7.5.3 requests a single record's allocation and wedges the bus on more. Honor the CDB[1..2] byte offset; dequeue only when the read reached the packet's end or CDB[5] is nonzero (0x0ea2-0x0f17); allocation <= 6 is a header-only peek without dequeue (0x0ea0). - Blind: multi-record batch. Cap at 16 records to bound the bus hold (device ROM: v1.3 unbounded, v2.0 capped at 200, 0x0d5c). Zero terminator record only when a capped batch promised more (0x0c1c); none at end of queue (0x0cb0). Never truncate a mid-batch record; no batching after a peek, offset, partial, or truncated read. - Both modes: 0x10 'more pending' flag reported truthfully from queue occupancy (0x0ca6, 0x0df2); previously never set in polled mode. Allocation-length payload cap applies per record, not cumulatively (0x0cf2, 0x0e8a). Dequeue a ring slot only after its payload left the bus (0x0d4c, 0x0f17), also fixing a concurrent Wi-Fi enqueue overwriting a slot mid-transfer. - 75/300 us gaps unchanged: no timed delays exist in the ROM; the measured gaps (e7d9629) are emergent Z180/DMA overhead that software-timed blind hosts depend on. The host driver selects blind purely on Virtual Memory being off (Gestalt 'vm '), independent of machine type, so a Mac Plus (68000, no VM) always drives blind and relies on these gaps. Measured RX: Mac SE/30 (System 7.5) 90 KB/s polled / 100 KB/s blind; Atari Falcon (FreeMiNT, blind) 100 KB/s. DaynaPORT 7.5.3 sends 0x80 (polled) with VM on, 0xC0 (blind) with VM off.
…[1..2] Per the SL003 device ROM (v2.0 0x1294, v1.3 0x11d7), WRITE(6) dispatches on CDB[5] bit 7: clear = one raw packet of cdb[3..4] bytes, set = the length-prefixed record stream. The previous test (cdb[5] == 0) parsed any other bit-7-clear value as a stream, misreading the first 4 payload bytes as a record header. The record stream format itself (4-byte header, zero-length terminator, cdb[3..4] ignored) matches the ROM exactly as implemented. Also reject writes with nonzero CDB[1..2] as the device does (ROM 0x1281).
7db3dfa to
a884515
Compare
|
Updated with new insights from the complete ROM disassembly. The main difference is that polled mode is now strictly 1 record only as per the original ROM. The Mac polled driver sizes each transfer for a single record and stalls hard if you batch more. The disassembly also pinned down a bug introduced by the mode-gating in 7c20579: in polled mode it forced the loop to exit before computing the header flags, so the "more packets pending" bit (0x10) was always 0. Polled hosts were never told to come back, so they fell to slow timer-polling, that's almost certainly the real cause of the slowdown in #346. With both fixes together (one record per transfer and a truthful pending flag from actual queue occupancy) the host re-reads immediately and polled mode stays fast: measured 90 KB/s on a Mac SE/30 with VM on, vs. single digits when either piece is wrong. |
|
Wow, this PR is insane! Have a PB Duo 270c running System 7.1 and connecting to an Appletalk Share (not TCP). I went from around 24 kb/s transfer speeds with the newest BlueSCSI firmware to 80-149 kb/s with the pull request binary (according to Connectix Speed Doubler) 😄 I can basically store many of my apps on my server now and run them directly from there! Wild! This is awesome! 🙏 🏎️ 😎 And this is on the Pico V1 PowerBook BlueSCSI V2. Awesome work @ingpaschke! |
|
@ingpaschke This seems good on the surface, though I think we need to test on a 68000 machine like the mac plus. Also I think we need to update the DaynaPORT docs (maybe port them over from the PiSCSI wiki) with findings. Could you also publish the reverse engineering/dissembled ROM's to a repo for reference? It is good we now have tools to quickly RE actual ROM and Drivers vs relying solely on observation - but we need to capture that in the reference docs now as well in case those tools get it wrong. We also need to understand why @jcs observed multipacket on a mac plus with no VM with the stock driver. So my two action items are:
|
|
@erichelgeson I previously only reverse engineered the relevant SCSI-commands, but why not do the whole ROM: https://github.com/ingpaschke/daynaport-scsilink/ That also suggests some further improvements around promiscuous mode, performance counters and multicast support that we could tackle later. I tested this code on 7.01 (but not System 6) on my MacPlus. It works reliably there. Multipacket transfers are normal on any Mac with VM disabled. The Macintosh driver gates this only on this distinction (Gestalt(' vm ')) |
|
I discovered a major deviation from the real device's behavior in my code (blind mode broken on Mac). Not data corruption broken, but only doing one packet per batch broken. I have to dig further into that tomorrow. Please don't merge for now. |
|
I tested on a PowerBook 100 with System 6 and a Pico 1 W and copied an 800K disk image over AppleShare. v2026.03.01 with my original multi-packet changes copied in 14 seconds, and with v2026.04.27 it took 30 seconds. So 7c20579 seems like it was the wrong fix. |
|
The problem I'm currently investigating is that I unwittingly modified the blind mode to use a transfer size budget that allows the target to put more than one record into a single SCSI transaction because I didn't understand the payload cap fully. The real device doesn't work that way, the payload cap is practically meaningless there (set it to less than 1524 -> packets get truncated, set it to more=it's capped to the record size anyway). Either way the device keeps streaming records; the allocation never bounds the transaction. The original Macs had to do byte level SCSI-handshaking in software which was slow, so they offered "blind mode" as an alternative: With SCSIReadBlind, you could just read at presumed maximum speed and just had to do the handshaking yourself or hope that it just somehow worked out. More modern Macs and all other hardware that I know of didn't have that problem. SCSI transfers use hardware handshaking and DMA transfers. So all other initiators should have no problems with blind mode? Yes, but there's a catch: The Mac can do multiple SCSI reads inside a single SCSI Command, so it use one read to just read the record header, then read the payload, hand off the payload to the IP stack and then loop and reuse that single 1524 byte buffer over and over again. That's what the two pauses are for: Read the header -> allow 75us to parse it and set up the next read -> read payload -> wait 300us for the Mac to hand off the Ethernet-packet and set up the next read -> loop... On most other SCSI-APIs like the Atari's, that's not possible. A SCSIRead is a whole single SCSI Command. I can't peek the header first and I can't reuse the per-record buffer. Moreover, the SCSI/Link runs a true streaming protocol: The Network device runs concurrently and can fill up the buffer (which seems to be about 14.8 KB / 9 full size Ethernet frames btw.) while SCSI drains it. That allows it to stream an unbounded number of incoming packets to the host (Firmware 2.0 capped that to 200 for some reason). But even if I set up my SCSIRead on the Atari to accept 200 packets and modify BlueSCSI to allow for that, I would then have a ~300 KB pile of Ethernet packets handed to the IP stack in one burst, every ACK delayed by the whole transfer, which wrecks TCP's flow-control timing (and a 300 KB SCSI buffer is not even representable in my driver's 16-bit build). So blind transfer mode is strictly Mac only. All other initiators have to make do with polled mode. I have to see how fast I can get this on my Falcon. If it's around the 100KB/s I now get with the "wrong" implementation, I'll just settle for that. Otherwise I'd like to propose a third mode that the real hardware didn't have that works along the lines of what I submitted above. Give me some time to work that out. The good news is: For Macs, the accurate version should be even faster, since it truly enables multi-record streaming for the first time. |
Started as an effort to improve DaynaPort SCSILink throughput on an Atari Falcon (stuck ~11 KB/s), which led to disassembling Dayna's original Mac
.ENET0driver (DaynaPORT 7.5.3) to see what the real hardware does. The real SCSI/Link returns a stream of[len|flags|payload|crc]records per READ(6) with a per-record "more pending" flag, and the host chains reads until it clears.This makes the handler match that protocol. Three standalone commits:
Pico_2_DaynaPORTonly (RP2350 has the RAM).Measured with the paired FreeMiNT driver:
Ring change is scoped to the Pico 2 W target; RP2040 and Ultra untouched. The record-stream + terminator match what the Mac driver already expects, so existing setups are unaffected (the terminator also fixes a latent stale-buffer read).