circuitpython: OSError: [Errno 5] Input/output: macOS Sonoma is delaying writes on small filesystems
CircuitPython version
Adafruit CircuitPython 8.2.6 on 2023-09-12; Adafruit CircuitPlayground Express with samd21g18
Code/REPL
# Write your code here :-)
print("Hello World!")
Behavior
Code stopped by auto-reload. Reloading soon.
soft reboot
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.
code.py output:
OSError: [Errno 5] Input/output error
Code done running.
Description
- Error on each save
Additional information
On a fresh installation of MacOS sonoma every time that code.py is saved a OSError: [Errno 5] Input/output error pops up, eventually the code runs but it takes about 15 seconds, I have tried disabling USB Stores and spotlight with:
defaults write DSDontWriteUSBStores -bool false
sudo mdutil -i off -d /Volumes/CIRCUITPY/
No luck
About this issue
- Original URL
- State: closed
- Created 9 months ago
- Reactions: 1
- Comments: 155 (6 by maintainers)
Since the original bug appears to have been fixed, though still with problems, closing in favor of #8918, which describes the new problems. We can reopen this if the original problem reappears. This issue is getting unwieldy due to the number of comments.
I wrote this shell script that remounts the USB drive on the MatrixPort S3. The “sleep 2” is a cluge that I don’t know why is needed, but if you unmount and then immediately try to mount the disk it fails.
My theory panned out, the reason that behavior differs after a manual
mount
of the filesystem is that it brings in kernel space kextcom.apple.filesystems.msdosfs
which then gets used instead of the new user space driver.The older kext does not delay meta data writes while the newer user space driver does. MacOS Sonoma has two entirely different fat filesystem drivers, which one gets used depends on the mechanism used to mount the filesystem.
The delay is fairly long, on the order of 10 to 20 seconds. I can see one shot of writes when I save the file, the delay, then the remaining writes:
Unmounting then remounting the
CIRCUITPY
filesystem like this:temporarily eliminates the delay. The special device name will vary. There should be a way to make this change permanent in Sonoma’s
/etc/fstab
, but so far I’m not finding aLABEL=
solution works.On behalf of all of us with this issue, our thanks for staying on top of this. On Jan 3, 2024 at 8:03 PM -0500, John Romkey @.***>, wrote:
If it helps anyone, this is the script I’ve been using lately. Taken from comments above. Works with bootloader disks by giving it the diskname as argument (
remount-circuitpy.sh FEATHERBOOT
)@romkey and @dahlbert I can confirm that 14.4 beta 2 fixes this issue. Tracing shows meta-data updates now follow a data write within a few hundred milliseconds. Like this:
If anyone on this issue hasn’t already done so, file an issue using Apple’s Feedback Assistant app. When you do, please mention FB13226668 (the issue I opened) so that Apple ties the reports together and raises the priority of the issue. If you’ve already opened an issue, you can append a note mentioning my issue number. Thank you!
The awkward moment when you experienced this in July and asked about it in Discord, but forget to file an actual issue, and then Sonoma ships…
Can I suggest that in the meantime, relevant CP/Adafruit guides get updated with a little note about Sonoma issues with a link to a page noting the workarounds? I don’t know what the % of CP users on macOS are, but I’d assume it’s more than a handful of us, and Sonoma has been out for a couple of weeks now.
As suggested by @romkey I’ve opened a new issue (#8918) to track the performance regression in Sonoma 14.4 beta 2 and above.
I have updated the Adafruit blog post and added a reply to the
@circuitpython
post on Fosstodon.By the way, the report I submitted to Apple way back on 10/3/2023 (FB13226668) now shows, “Resolution: Potential fix identified - for a future OS update”.
There was some question somewhere (not here, it seems) about whether the MSC device is reported as “removable”. The answer is that TinyUSB in its MSC device code always sets the RMB (“removable”) bit to 1 when it answers a SCSI INQUIRY command.
@romkey I’m perplexed, too. But, I have a theory. Earlier @jeremyamoore noted that Sonoma had moved fat filesystems from kernel to user space. I’d missed that entirely, so all my reading of the kext code never turned up a plausible bug. It’s possible that the automatic mount is using the new user space filesystem code while a command line
mount
is pulling in the older kernel extension (kext). Now to devise an experiment to see if that’s the case.It was giving exactly the same symptoms, but flash nuke and fresh CircuitPython fixed the problem. Also tried another flash nuke and downgrading to 7.0.0 in case it was an issue left over from an older filesystem, but still fine. Must have been random filesystem corruption or something. Sorry for the false alarm!
I would like to consolidate these, and perhaps extract the most pertinent posts from each issue into a new shorter issue, since this issue already has hidden posts because it is so long.
Verified that a board with >8M of filesystem flash will not suffer delayed writes by testing with an Adafruit RP2040 Metro with 16M flash (about 15M FAT16 filesystem).
Besides the simple copying of tiny
foo.txt
, I also copied a substantial batch of .py files, and could not get the error. But they are all pretty small files, I think. So I’m not worried we are getting different results. Since it’s still reproducible on real boards, unfortunately, we’ll just have to keep trying the betas.Easier way to verify, doesn’t even need a USB drive.
Create an 8MB FAT12 disk image. Not sure how to do this on macOS, on Linux it’s:
Transfer this to your Mac.
Mount it - either open Disk Utility in the Finder and use File | Open Disk Image…
or
This should mount as
/Volumes/NO NAME
and appear in the sidebar in the Finder.Copy small files to it from the command line or using Finder. You should see errors.
You can unmount it by ejecting the volume in Finder.
I haven’t confirmed if the filesystem is corrupted when errors happen during the copy, so it’s best to always start with a fresh disk image to be sure you’re not accidentally seeing actual filesystem errors.
@romkey That’s super helpful. Thank you!
@eightycc I tried again with a few other sizes. 8MB fails while 8.5MB, 10MB, 12MB and 16MB succeed (worked backwards on this, I’m not completely erratic).
Just to be sure I rebooted and 8.5MB still succeeded.
I tried 8MB three times with fresh filesystems and it failed each time.
I’m going to take a wild guess that 8MB is the breakpoint.
@romkey It’s fascinating and unexpected that filesystem size makes a difference, thank you for running that down. So that I can get trace data, I’m going build a modified CP that reports a 64M device. Also, thanks for the confirmation on the work around.
@romkey You should be able to work around any of the FAT filesystem write problems by unmounting, making a mount point, and then re-mounting the device. No need to specify mount option flags. Give it a try and let me know if you run into a case where that doesn’t work.
@romkey note that @eightycc’s testing seems to show that any manual remount with any flags seems to clear the problem. Your last two tests probably included a manual remount, so maybe that explains what you are seeing.
Some workarounds:
supervisor.runtime.autoreload = False
.sync
in a Terminal windowos.sync()
in the right place in whatever editor is being used.I will not have an opportunity to try this on a Sonoma Mac for a few days, but will try to characterize the issue more exactly then by doing some USB monitoring.
Reported in forums: https://forums.adafruit.com/viewtopic.php?t=205044
@dhalbert Earlier you’d asked whether
sync
forces proper behavior on MacOS Sonoma. Yes, it does. I’ve verified that both the command linesync
and pythonos.sync()
force updates to the Express filesystem immediately.I did a small survey and found that the Mu IDE is an entry point for beginners. So, I added an
os.sync()
towrite_and_flush()
inlogic.py
of Mu 1.2.0. With the patch, savingcode.py
works as expected, i.e., the file and all directory and FAT updates are written out immediately.I also traced file update on Windows 10 22H2 and Debian Bookworm 13 kernel 6.1.52 running Mu 1.2.0 with and without the patch. In all cases saving the file writes the file and all directory and FAT updates immediately on save.
@dahlbert Under the covers what’s going on is that MacOS copy-on-reads blocks of a file into virtual memory and backs that memory with an unallocated area of the underlying filesystem. Writes become stores into virtual memory, triggering immediate page-outs to the filesystem. Because the filesystem mount flags include
MNT_ASYNC
, updates to the FAT and the metadata file are deferred until a timeout mechanism forces a flush. This is why pulling the stick results in a zero length file, the file data is there but the FAT update to point to it hasn’t happened yet.This got me thinking that if
autoreload
were to trigger selectively on just writes to the FAT (probably just two on such a small file system) this would eliminate the I/O error at the expense of a delayedautoreload
.I’ve got an older Monterey (10.12) machine I can test and trace.
My guess is delayed write. I see this occasionally with Linux too. I run
sync
from the command line and then the next reload works as expected.