Last week was Hackweek, a week full of fun and innovation at SUSE. I decided to use the time to work on a USB storage gateway for Ceph.

The concept is simple - create a USB device that, when configured and connected, exposes remote Ceph RADOS Block Device (RBD) images for access as USB mass storage, allowing for:
- Ceph storage usage by almost any system with a USB port
- Including dumb systems such as TVs, MP3 players and mobile phones
- Boot from RBD images
- Many systems are capable of booting from a USB mass storage device
- Minimal configuration
- Network, Ceph credentials and image details should be all that's needed for configuration
Hardware
I already own a Cubietruck, which has the following desirable characteristics for this project:- Works with a mainline Linux Kernel
- Is reasonably small and portable
- Supports power and data transfer via a single mini-USB port
- Performs relatively well
- Dual-core 1GHz processor and 2GB RAM
- Gigabit network adapter and WiFi 802.11 b/g/n
Possible alternatives worth evaluation include C.H.I.P (smaller and cheaper), NanoPi2, and UP (faster). I should take this opportunity to mention that I do gladly accept hardware donations!
Base System
I decided on using openSUSE Tumbleweed as the base operating system for this project. An openSUSE Tubleweed ARM port for the Cubietruck is available for download at:http://download.opensuse.org/ports/armv7hl/factory/images/openSUSE-Tumbleweed-ARM-JeOS-cubietruck.armv7l-Current.raw.xz
Installation is as straightforward as copying the image to an SD card and booting - I documented the installation procedure on the openSUSE Wiki.
Releases prior to Build350 exhibit boot issues due to the U-Boot device-tree path. However, this has been fixed in recent builds.
Kernel
The Linux kernel currently shipped with the openSUSE image does not include support for acting as a USB mass storage gadget, nor does it include Ceph RBD support. In order to obtain these features, and also reduce the size of the base image, I built a mainline Linux kernel (4.4-rc4) using a minimal custom kernel configuration:~/> sudo zypper install --no-recommends git-core gcc make ncurses-devel bc ~/> git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ~/> cd linux ~/linux/> wget https://raw.githubusercontent.com/ddiss/ceph_usb_gateway/master/.config # or `make sunxi_defconfig menuconfig` # ->enable Ceph, sunxi and USB gadget modules ~/linux/> make oldconfig ~/linux/> make -j2 zImage dtbs modules ~/linux/> sudo make install modules_install ~/linux/> sudo cp arch/arm/boot/zImage /boot/zImage-$(make kernelrelease) ~/linux/> sudo cp arch/arm/boot/dts/sun7i-a20-cubietruck.dtb /boot/dtb-4.3.0-2/ ~/linux/> sudo cp arch/arm/boot/dts/sun7i-a20-cubietruck.dtb /boot/dtb/
This build procedure takes a long time to complete. Cross compilation could be used to improve build times.
I plan on publishing a USB gadget enabled ARM kernel on the Open Build Service in the future, which would allow for simple installation via zypper - watch this space!
Ceph RADOS Block Device (RBD) mapping
To again save space, I avoided installation of user-space Ceph packages by using the bare kernel sysfs interface for RBD image mapping.The Ceph RBD kernel module must be loaded prior to use:
# modprobe rbd
Ceph RADOS block devices can be mapped using the following command:
# echo -n "${MON_IP}:6789 name=${AUTH_NAME},secret=${AUTH_SECRET} " \
"${CEPH_POOL} ${CEPH_IMG} -" > /sys/bus/rbd/add
$MON_IP can be obtained from ceph.conf. Similarly, the $AUTH_NAME and $AUTH_SECRET credentials can be retrieved from a regular Ceph keyring file.
$CEPH_POOL and $CEPH_IMG correspond to the location of the RBD image.
A locally mapped RBD block device can be subsequently removed via:
# echo -n "${DEV_ID}" > /sys/bus/rbd/remove
$DEV_ID can be determined from the numeric suffix assigned to the /dev/rbdX device path.
Images can't be provisioned without the Ceph user-space utilities installed, so should be performed on a separate system (e.g. an OSD) prior to mapping on the Cubietruck. E.g. To provision a 10GB image:
# rbd create --size=10240 --pool ${CEPH_POOL} ${CEPH_IMG}
With my Cubietruck connected to the network via the ethernet adapter, I observed streaming read (/dev/rbd -> /dev/null) throughput at ~37MB/s, and the same value for streaming writes (/dev/zero -> /dev/rbd). Performance appears to be constrained by limitations of the Cubietruck hardware.
USB Mass Storage Gadget
The Linux kernel mass storage gadget module is configured via configfs. A device can be exposed as a USB mass storage device with the following procedure:# modprobe sunxi configfs libcomposite usb_f_mass_storage # mount -t configfs configfs /sys/kernel/config # cd /sys/kernel/config/usb_gadget/ # mkdir -p ceph # cd ceph # mkdir -p strings/0x409 # echo "fedcba9876543210" > strings/0x409/serialnumber # echo "openSUSE" > strings/0x409/manufacturer # echo "Ceph USB Drive" > strings/0x409/product # mkdir -p functions/mass_storage.usb0 # echo 1 > functions/mass_storage.usb0/stall # echo 0 > functions/mass_storage.usb0/lun.0/cdrom # echo 0 > functions/mass_storage.usb0/lun.0/ro # echo 0 > functions/mass_storage.usb0/lun.0/nofua # echo "$DEV" > functions/mass_storage.usb0/lun.0/file # mkdir -p configs/c.1/strings/0x409 # echo "Config 1: mass-storage" > configs/c.1/strings/0x409/configuration # echo 250 > configs/c.1/MaxPower # ln -s functions/mass_storage.usb0 configs/c.1/ # ls /sys/class/udc > UDC$DEV corresponds to a /dev/X device path, which should be a locally mapped RBD device path. The module can however also use local files as backing for USB mass storage.
Boot-Time Automation
By default, Cubietruck boots when the board is connected to a USB host via the mini-USB connection.With RBD image mapping and USB mass storage exposure now working, the process can be run automatically on boot via a simple script: rbd_usb_gw.sh
Furthermore, a systemd service can be added:
[Unit] Wants=network-online.target After=network-online.target [Service] # XXX assume that rbd_usb_gw.sh is present in /bin ExecStart=/bin/rbd_usb_gw.sh %i Type=oneshot RemainAfterExit=yes
Finally, this service can be triggered by Wicked when the network interface comes online, with the following entry added to /etc/sysconfig/network/config:
POST_UP_SCRIPT="systemd:rbd-mapper@.service"
Boot Performance Optimisation
A significant reduction in boot time can be achieved by running everything from initramfs, rather than booting to the full Linux distribution.Generating a minimal initramfs image, with support for mapping and exposing RBD images is straightforward, thanks to the Dracut utility:
# dracut --no-compress \ --kver "`uname -r" \ --install "ps rmdir dd vim grep find df modinfo" \ --add-drivers "rbd musb_hdrc sunxi configfs" \ --no-hostonly --no-hostonly-cmdline \ --modules "bash base network ifcfg" \ --include /bin/rbd_usb_gw.sh /lib/dracut/hooks/emergency/02_rbd_usb_gw.sh \ myinitrd
The rbd_usb_gw.sh script is installed into the initramfs image as a Dracut emergency hook, which sees it executed as soon as initramfs has booted.
To ensure that the network is up prior to the launch of rbd_usb_gw.sh, the kernel DHCP client (CONFIG_IP_PNP_DHCP) can be used by appending ip=dhcp to the boot-time kernel parameters. This can be set from the U-Boot bootloader prompt:
=> setenv append 'ip=dhcp'
=> boot
The new initramfs image must be committed to the boot partition via:
# cp myinitrd /boot/
# rm /boot/initrd
# sudo ln -s /boot/myinitrd /boot/initrd
Note: In order to boot back to the full Linux distribution, you will have to mount the /boot partition and revert the /boot/initrd symlink to its previous target.
Future Improvements
- Support configuration of the device without requiring console access
- Run an embedded web-server, or expose a configuration filesystem via USB
- Install the operating system onto on-board NAND storage,
- Further improve boot times
- Avoid U-Boot device probes
- Experiment with the new f_tcm USB gadget module
- Expose RBD images via USB and iSCSI
Credits
Many thanks to:- My employer, SUSE Linux, for encouraging me to work on projects like this during Hackweek.
- The linux-sunxi community, for their excellent contributions to the mainline Linux kernel.
- Colleagues Dirk, Bernhard, Alex and Andreas for their help in bringing up openSUSE Tumbleweed on my Cubietruck board.
Edits
- 2025-04-02: fix architecture image background