For the same reasons as many others, 2G/3G/Satellite connections which are
slow & expensive & intermittent, I am looking into downloading only binary
deltas as well as a robust resume download feature. Swupdate already has a
resume download function, but it will only resume the download if the
swupdate process hasn't been stopped. I'd prefer it to be able to resume
after a next boot as well. And that requires a place to store the partially
downloaded file.
Before going into the details of the idea, lets start with a short summary
on how we'll use swupdate for Venus [1]. We have two rootfs partitions: one
active, one standby. Not read only. Besides that there is a data partition
for user settings and such. Active partition runs our application, and has
swupdate and the scripts around swupdate. There are two ways of updating the
device:
1) Online: the wrapper script around swupdate uses curl to check if there is
a new version available for download. If there is a new one, it starts
swupdate with the -d and -install-direct options: download the file, and
stream it into passive partition(s). Once completed, it boots into that by
swapping passive and active.
2) Offline: user inserts sdcard or usb stick with a swu file, and somehow
triggers the device to open that swu file and apply it to the passive
partition again.
For those interested in the implementation, Mans from Denx is currently
finalizing the implementation and latest status is visible in [2] and [3].
Now, the details of my thinking about these binary deltas:
We'll need extra space on disk to store the (unzipped!) original contents of
the swu file. Here is why:
- to apply a delta, you need an original
- since we don't want a read only rootfs, it needs to be stored separately
- to prevent the deltas slowly growing, the original needs to be updated
as well on every update.
- storing it zipped means zipping it on the device, which in our case is
a bad idea: far too CPU and RAM intensive.
- one thing that could probably be done is to take out all the zeros of the
vfat and ext4 files (ie. sparse). That would save quite some space now,
but the added value of this will decrease when the in-use size of the
rootfs grows. So to calculate worst case, we need to calculate with 100%
in-use values.
xdelta3, and perhaps also other binary diff tools, are able take the delta
from a stream. But, since we want to be able to resume download also after a
power cycle, this can't be used: make sure we can also store a delta, with max size same as swu file without compression. So, together with that original, the worst case necessary disk space is 2 X the size of one
uncompressed rootfs + bootloader + kernel.
Then, after downloading the delta we need to apply it. Applying in-place is
quite a challenge (Jeroen H. looked quite deeply into that). So that leaves
us with only one option: writing the delta to a third file. So now max
required size is 3 X the size of one unzipped rootfs + bootloader + kernel
etc.
Note that in case you have plenty RAM, it would also be possible to store
the delta file in RAM: worst case disk is 2X and RAM is 1X.
And, another alternative, it would also be possible to store the new file in
RAM first. And when done replace the original in disk with that. It does
create a (short) window in which a loss of power will mean losing
everything. Not the end of the world: the device will need to re-download
the full new file. For this case, worst case disk is 1X and RAM is 2X.
So, I invite you to shoot some holes in that logic!
Lets for now assume it is flawless :-), and therefore continue with the
implementation. Which is surprisingly simple to do:
- instead of using swupdate -d, we use curl or something else to download,
and resume download, the files to disk.
- on the disk there is orignal.swu
- if it is not there, or its md5 is wrong, skip the whole delta process and
download latest version completely. Store it for next time, and call
swupdate -i. Done.
- if the original is there, download the delta
- apply the delta, result is two files: myimg-[buildtime-old].swu and
myimg-[buildtimenew].swu
- remove original.swu (in previous step we md5-ed the new file first of-
course).
- use swupdate -i to update the image. Done.
- in case anything goes wrong, revert to full download, perhaps even just
swupdate -d, instead of curl/disk. Done.
All this implementation can be done outside swupdate, in a simple bash
-script. I've something running, I'll post it once a bit further
progressed. And I don't even see any functional improvements in the
diskspace or RAM requirements by implementing it into swupdate. Ofcourse,
to make using this easy, it would be nice to add it into swupdate instead of
some wrapper scripts.
Am I overlooking something? Looking forward to hear your thoughts on this!
Best regards, Matthijs
FYI: In my first approach, I was using swu files containing zipped
contents, and that changes, and complicates!, everything. There are then
many different ways to implement this, and implementing it within swupdate,
or at least being able to stream a swu file via stdin to swupdate does have
its advantages in that case. But, as explained, that also requires rezipping
the new file to store it, which is too resource intensive. Better use some
extra storage, for example explain the end customers that if he/she is on an
expensive and/or slow internet connection, he/she should leave an sdcard
inserted into the device. Cost of an sdcard is nothing compared to
downloading a few 100 MBs on an expensive internet connection.
[1] https://github.com/victronenergy/venus/wiki
[2] https://github.com/victronenergy/meta-victronenergy/commits/swupdate-bbb
[3] https://github.com/victronenergy/venus/wiki/swupdate-project