Strict limit of write cache / 0s sync time policy for usb devices by default

Hello folks,

I know it was controversial in the past what would be the best solution. In file manager you would see a fast transfer speed, then it seems to stuck at 75%. It depends on how much RAM you have and how big your write cache is.

There are 3 approaches:

  1. Force to use sync instead of async as a mount option when using udisks2 aka your file manager, which used it in the background:
SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ENV{UDISKS_MOUNT_OPTIONS_DEFAULTS}+="sync", ENV{UDISKS_MOUNT_OPTIONS_ALLOW}+="sync"
$ dd status=progress if=/dev/urandom of=/run/media/user/USBSTICK/test_sync.img bs=1M count=1000
1046478848 bytes (1,0 GB, 998 MiB) copied, 304 s, 3,4 MB/s
1000+0 records in
1000+0 records out
1048576000 bytes (1,0 GB, 1000 MiB) copied, 304,593 s, 3,4 MB/s

Need to be noted:

       sync
           All I/O to the filesystem should be done synchronously. In the case of media with a limited number
           of write cycles (e.g. some flash drives), sync may cause life-cycle shortening.
For understanding what it actually does for "Windows users":

While sync is default for usb devices on Windows, it is not on Linux. async is default for any device if not set otherwise.

  1. Lower/restrict globally can optimize it a bit, but affects all block devices, which is not optimal. It also writes fast and slow down after a while, but slight smoother.

Settings from maxperfwiz, when using 32GB RAM:

vm.dirty_bytes=419430400
vm.dirty_background_bytes=209715200
vm.dirty_expire_centisecs=3000
vm.dirty_writeback_centisecs=1500
vm.min_free_kbytes=311132
$ dd status=progress if=/dev/urandom of=/run/media/user/USBSTICK/test_dirty.img bs=1M count=1000 && time sync
1045430272 bytes (1,0 GB, 997 MiB) copied, 130 s, 8,0 MB/s
1000+0 records in
1000+0 records out
1048576000 bytes (1,0 GB, 1000 MiB) copied, 130,642 s, 8,0 MB/s

Sure, it is about 3x faster compared to ~300sec, but the sync time need to be added:

sync  0,00s user 0,01s system 0% cpu 1:07,53 total

So it is about 131s + 68s ~ 199s. Still faster than the first method with the sync mount option, but you cannot eject the flash drive immediately and have to wait 1 minute, that is not ideal.

  1. Using Backing Device Information (BDI) subsystem for redefining the values per device:

Actually used for fuse or network devices, which are commonly slower, it perfectly fits now slower USB devices.

I followed this: [root tip] [How To] Disable write cache for USB storage devices

$ dd status=progress if=/dev/urandom of=/run/media/user/USBSTICK/test_bdi.img bs=1M count=1000 && time sync
1045430272 bytes (1,0 GB, 997 MiB) copied, 244 s, 4,3 MB/s
1000+0 records in
1000+0 records out
1048576000 bytes (1,0 GB, 1000 MiB) copied, 244,465 s, 4,3 MB/s
sync  0,00s user 0,02s system 29% cpu 0,076 total

A slight speed up by ~50s compared to the sync option, and you can immediately unplug the flash drive. While still, the async ~50sec faster.

  1. Without any optimizations:
$ dd status=progress if=/dev/urandom of=/run/media/user/USBSTICK/test_none.img bs=1M count=1000 && time sync
1044381696 bytes (1,0 GB, 996 MiB) copied, 107 s, 9,8 MB/s
1000+0 records in
1000+0 records out
1048576000 bytes (1,0 GB, 1000 MiB) copied, 107,697 s, 9,7 MB/s
sync  0,00s user 0,01s system 0% cpu 1:13,58 total

107s + 74s = 181s

In simple words:

Method total time sync time
sync mount option ~300s ~0s
BDI restriction ~254s ~0s
optimized dirty pages ~199s ~68s
no optimizations ~182s ~74s

I know, I know, these tests don’t reflect every setup, but in general it is a test of all methods with a slow device (actually write speed ~4MB/s). async is about 1/3 faster in total, but the sync time is too high for a flash drive. In that case, a sync was forced, imagine how long it takes when it isn’t forced.

I would really see that a 0s sync time would be the default for Manjaro for every usb device. Either the sync option or the BDI limitation. The BDI limitation would certainly be a better choice.

I would argue by saying that flash drives are hot plug devices that can be unplugged immediately. Even professionals sometimes forget this and forget that there is still writing going on in the background what leads to data corruptions.

Better security than performance by default when it comes to data transfers.

Probably add an option to manjaro-settings-manager would ideal, where you can set this option like an on/off toggle.

Thanks for reading.

References:

3 Likes

I also don’t like this background sync for USB to me it is non sense. It should not show the transfer is finished when it is not. 99% of users would trust the GUI saying it is finished when it definitely is not.

But I’m not sure there is a proper good option, probably AUR (en) - udev-usb-sync is good enough, but having a new default is probably not happening, Manjaro team will have people for, and people against it.

An option in Manjaro Settings Manager to disable USB Caching could be OK indeed.

4 Likes

instead of doing dirty hacks just pull the file manager code and add a sync command before the transfer GUI exits, this way it’s done properly.

Why this is not default is just astonishing.

1 Like

Well, actually it is not the job of the file manager, but the kernel. The file manager just uses the API of the kernel. If it used a sync request, then this would a workaround in user space, but actually the kernel should handle that.

The reason for all of that simply, that the default is made for fast devices on server racks etc., not for slow devices and especially not for desktops, that are exceptions that require a rule.

The larger the RAM and the slower the connection speed, the longer the synchronization takes. So limiting the write buffer in the RAM makes totally sense here.

It is an never-ending story - and yes - the default Linux behavior with file cache - often surprises even hardend users :grin:

When I created the topic qouted - it was because the optimization done in other topics - and the result were highly depending on the system’s hardware configuration and it was impossible to create a one size fits all.

BDI can only be used on kernels newer than than 6.2.

Your udev rule targets usb subsystem and very specific usecase - to in theory the rule should not affect other devcies like SATA or NVMe - is that also your opinion ?

That is of course - unless they are internall attached using a usb bus - but that would be rare I assume ?

Technically the package I created udev-usb-sync could implement the rule and the package would have a broader scope - am I totally of track here ?

No, the mount options is default, least common denominator behavior.

Since the file manager knows this is not what a file manager user expects, it should issue sync before declaring transfer finished. It’s obvious, why you argue?

Nope… since v2.6 (Jan 2008): https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-bdi, but only

  • /sys/class/bdi/<bdi>/
  • /sys/class/bdi/<bdi>/read_ahead_kb
  • /sys/class/bdi/<bdi>/min_ratio
  • /sys/class/bdi/<bdi>/max_ratio
  • /sys/class/bdi/<bdi>/stable_pages_required

Since v6.0/6.1 (Oct/Nov 2022):

  • /sys/class/bdi/<bdi>/min_ratio_fine
  • /sys/class/bdi/<bdi>/max_ratio_fine
  • /sys/class/bdi/<bdi>/min_bytes
  • /sys/class/bdi/<bdi>/max_bytes
  • /sys/class/bdi/<bdi>/strict_limit

Yes it should only affect usb devices. No SATA, no NVME, only the usb connection matters, which is a hotplug device.

Sure, the mentioned udev rule works great, and it is what I used when testing. Whoever found it first. Thanks.


$ for x in 12 480 5000 10000; do printf "%.0f\n" `echo "(($x / 8) * 0.01) * 1024 * 1024" | bc`; done
10486
629146
6553600
13107200

These values seem to be good and stable. It buffers 0,01s of the current bandwidth.

# Works since linux 6.1
ACTION=="add|change", KERNEL=="sd[a-z]", ENV{ID_USB_TYPE}=="disk", RUN+="/usr/bin/bash -c 'echo 1 > /sys/block/%k/bdi/strict_limit'"
ACTION=="add|change", KERNEL=="sd[a-z]", ENV{ID_USB_TYPE}=="disk", ATTRS{speed}=="12",    RUN+="/usr/bin/bash -c 'echo 10485     > /sys/block/%k/bdi/max_bytes'"
ACTION=="add|change", KERNEL=="sd[a-z]", ENV{ID_USB_TYPE}=="disk", ATTRS{speed}=="480",   RUN+="/usr/bin/bash -c 'echo 629145    > /sys/block/%k/bdi/max_bytes'"
ACTION=="add|change", KERNEL=="sd[a-z]", ENV{ID_USB_TYPE}=="disk", ATTRS{speed}=="5000",  RUN+="/usr/bin/bash -c 'echo 655360    > /sys/block/%k/bdi/max_bytes'"
ACTION=="add|change", KERNEL=="sd[a-z]", ENV{ID_USB_TYPE}=="disk", ATTRS{speed}=="10000", RUN+="/usr/bin/bash -c 'echo 13107200  > /sys/block/%k/bdi/max_bytes'"

Alright. Then it’s your job to write to every developer of a file manager and convince them to use a sync request after every copy. Have a good time :slight_smile: Here, we cannot do anything in that case.

And no, it doesn’t only affect GUI’s, but also terminal applications.

1 Like

I am also very happy with udev-usb-sync and as far as i can understand, it does exactly this - force sync, it just uses hdparam as dependency.

If you use terminal you probably know enough to add && sync to your command line where applicable

If you want to argue against this, then you need to argue for something like: voluntary sync to the GUI, like a check box that says: only report transfer finished when it is actually copied to target and it’s safe to remove the target

In my mind GUI is meant for ease of use, and such deep internals should not be exposed. Just sync before dialog finishes.

1 Like

Not against it. Go for it, but again: This thread is the wrong place. Here you go:

and so on. Talk to them and provide a solution.

I’ll add here my final solution. Now, I have zero problem with any usb devices and the speed is good, and the progress bar is accurate on the GUI.

File: /etc/udev/rules.d/99-usb-sync.rules

KERNEL!="sd[a-z]", GOTO="limit_write_cache_end"
ENV{ID_USB_TYPE}!="disk", GOTO="limit_write_cache_end"
ACTION!="add|change", GOTO="limit_write_cache_end"


# If it is identified as a thumb drive / usb-stick force sync for udisks2
# ENV{ID_DRIVE_THUMB}=="1", ENV{ID_FS_USAGE}=="filesystem", ENV{ID_FS_TYPE}!=vfat, ENV{UDISKS_MOUNT_OPTIONS_DEFAULTS}+="sync", ENV{UDISKS_MOUNT_OPTIONS_ALLOW}+="sync", GOTO="limit_write_cache_end"

# 09 -> exclude speed of hubs
ATTRS{bDeviceClass}!="09", ATTRS{speed}=="10000", RUN+="/usr/local/bin/usb_bdi_limit %s{speed} %k", GOTO="limit_write_cache_end"
ATTRS{bDeviceClass}!="09", ATTRS{speed}=="5000",  RUN+="/usr/local/bin/usb_bdi_limit %s{speed} %k", GOTO="limit_write_cache_end"
ATTRS{bDeviceClass}!="09", ATTRS{speed}=="480",   RUN+="/usr/local/bin/usb_bdi_limit %s{speed} %k", GOTO="limit_write_cache_end"
ATTRS{bDeviceClass}!="09", ATTRS{speed}=="12",    RUN+="/usr/local/bin/usb_bdi_limit %s{speed} %k", GOTO="limit_write_cache_end"

LABEL="limit_write_cache_end"

File: /usr/local/bin/usb_bdi_limit

#!/usr/bin/env bash

LANG=C
LC_NUMERIC=C

SPEED="$1"
BLOCKDEVICE="$2"
KERNEL_MAJOR_VERSION=$(uname -r | awk -F'.' '{print $1}')


if [[ $KERNEL_MAJOR_VERSION -ge 6 ]]; then

    # the following rules are introduced with kernel 6.1
    # https://docs.kernel.org/admin-guide/abi-testing.html#abi-sys-class-bdi-bdi-strict-limit
    # https://docs.kernel.org/admin-guide/abi-testing.html#abi-sys-class-bdi-bdi-max-bytes

    BUFFER_TIME="0.05"
    SAFETY_FACTOR="1.3"
    BUFFER_SIZE=$(printf '%.0f' `echo "( ($SPEED / 8) * $BUFFER_TIME * $SAFETY_FACTOR) * 1024 * 1024" | bc`)

    # for x in 12 480 5000 10000; do echo -n "$x -> " ;printf "%.0f\n" ` echo "(($x / 8) * 0.05 * 1.3) * 1024 * 1024" | bc`; done
    # 62915
    # 4089446
    # 42593157
    # 85196800

    [[ -n $(which hdparm) ]] && $(which hdparm) -W 0 /dev/$BLOCKDEVICE
    echo "$BUFFER_SIZE" > /sys/block/$BLOCKDEVICE/bdi/max_bytes
    echo 1 > /sys/block/$BLOCKDEVICE/bdi/strict_limit

    echo ""

elif [[ $KERNEL_MAJOR_VERSION -le 5 ]]; then
    # the following rule is introduced with kernel 2.6
    # https://docs.kernel.org/admin-guide/abi-testing.html#abi-sys-class-bdi-bdi-max-ratio

    # 1% of available RAM ->  8046522kB -> 80.465kB -> 80MB
    echo 1 > /sys/block/$BLOCKDEVICE/bdi/max_ratio

fi
2 Likes

Could I persuade you to clone the Frede H / Udev Usb Sync · GitLab and incorporate your idea - there is no reason to duplicate efforts - and I have queried the team on the option of including it in the ISO root filesystem.

1 Like

Alright. I’ll send you a patch, since I have no access to Manjaro’s Gitlab anymore.

1 Like

I’ve let @romangg know about the general trend I’ve seen. It seems that those who are using OAuth via existing GitLab, GitHub credentials, etc. to login to the Manjaro GitLab are not able to.

EDIT: Clarity.

1 Like

I used to login with my GitHub account I think, I don’t know it’s been a long time there is no more this option.

1 Like

Thanks for the feedback, I’ve edited my reply above for more clarity.

Will this automatically come as an update of some core package, or as an update of aur udev-usb-sync, or will a new aur package be made, or should we apply manually (i have udev-usb-sync now)?

1 Like

When I have confirmed it - the mentioned custom package will be updated.

//EDIT:
On a joint effort the custom package has been updated to v0.9.

The same updates has been applied to the repo at Manjaro Gitlab.

4 Likes