Assignment 3: Tape Device

Announcement date: 05.05.2026

Warning

Make sure you have the latest version of QEMU:

commit 0855a81f6f1c6f5700e30cc9c5a46ce1b096fc10

Published on May 09.

After pulling, make sure to recompile QEMU. Otherwise, read/write commands may not work correctly with some pagetable configurations.

Due date: 02.06.2026 (final due date 16.06.2026)

Additional materials

Introduction

Storage is a hard problem that spawned many different hardware and software solutions, often tailored for a very niche, specific use case. One of these use cases is long-term data storage, for example, archives or backups.

A hardware solution often used for such archives is a tape library. While Using magnetic tapes for data storage may seem dated, new solutions based on tapes are still being developed. LTO-10 magnetic tape specification was released in January 2026, and it supports tapes with up to 40 TB of capacity. While the maximum speed of 400 MB/s may seem relatively low for today's SSD standards (and remember that tape is basically a rotational drive), the capacity is very impressive. A single tape library is meant to store many such tapes, allowing the user to reach petabytes of backup/archive capacity easily.

While such devices usually communicate over interfaces like Fibre Channel or SAS, our device consists of two parts: a PCI card that exposes a special proprietary interface, and the tape library itself, connected to the PCI card. To expose the functionality of the device to the operating system in a convenient way, you will use the abstraction of the block device layer.

In this task, you will implement a Linux PCI driver for this imaginary tape library, called tapedev. To make testing convenient, the tapes used by the device are small.

The kernel driver should expose the device as a block device. For every Tapedev device attached, it should create a /dev/tapedevX block device, where X is the number of the attached device, starting from 0. Every tapedev consists of up to 8 separate, independent sections. Each section of the tape device should be represented by a file named /dev/tapedevXsN where N is the number of the section starting from 0.

Block device interface

Your task is to write a block layer driver for this device. Each section of the device should be exposed as a separate block device, all tapes in a single section are meant to form a single continuous block device.

The driver should support REQ_OP_READ and REQ_OP_WRITE block layer operations, support for other operations is not required.

Each section file should also support two ioctl calls:

  • ioctl(TAPEDEV_IOCTL_GET_INFO) - copy information about section (number of tapes type of tape, currently inserted tape) into a structure in the userspace.

  • ioctl(TAPEDEV_IOCTL_EJECT_TAPE) - wait until driver stops processing current read/write request, eject tape (if inserted).

The device should also expose three read-only sysfs attributes:

  • /sys/block/tapedevXsN/tape/tape_type - type of the tape (from 0 to 4)

  • /sys/block/tapedevXsN/tape/tapes - number of tapes

  • /sys/block/tapedevXsN/tape/current_tape - currently inserted tape or 0

The driver should utilize zero-copy techniques wherever possible - the device should write the data directly into (DMA-mapped) biovec buffers. The driver should Support reading/writing at least 512 segments in a single request/command. It is permitted to handle a request that spans many tapes in multiple commands.

Hints

Useful sources:

The following functions may be useful:

  • register_blkdev

  • blk_mq_alloc_sq_tag_set

  • blk_mq_alloc_disk

  • set_capacity

  • device_add_disk

  • blk_rq_map_sg

Other hints:

  • It is okay to handle only a single block layer request at once.

  • You may want to be careful with DMA alignment restrictions.

  • You may want to use WARN macros to make sure that your assumptions hold during tests.

Solution format

The device driver should be implemented in C as a Linux kernel module, working with the lab's kernel version. The compiled module should be called tapedev.ko.

Submit an archive named ab123456.tar.gz (where ab123456 is your student's login). After unpacking, the package should create the ab123456 directory with the following contents:

  • the module source files

  • Makefile and Kbuild files—running make should build the tapedev.ko module

  • a README file with a brief description of your solution, including driver design choices (e.g., regarding locking, fences) and code structure

Grading

You can obtain up to 10 points. The assignment is graded based on automated tests and code review. The tests include the provided examples but also some other undisclosed tests that are variations of the provided examples

For the code review, points may be deducted for:

  • detected errors, e.g., regarding locking or memory leaks

  • minor deductions for issues like unclear or convoluted code structure or misusing the block layer interface

The driver may consist of a single source file.

Please try to adhere to the Kernel coding style. You may want to try scripts/checkpatch.pl, although remember that it requires the submission to be formatted as a patch, not as a .c file.

QEMU

Tapedev is implemented as a PCI device in QEMU.

To use the Tapedev device, a modified version of QEMU is required. It is available in source code form.

Warning

Make sure you have the latest version of QEMU:

commit 0855a81f6f1c6f5700e30cc9c5a46ce1b096fc10

Published on May 09.

After pulling, make sure to recompile QEMU. Otherwise, read/write commands may not work correctly with some pagetable configurations.

To compile it:

  • Clone the repository: https://gitlab.uw.edu.pl/zso/2026l-public/zad3-public.git

  • Ensure that the following dependencies are installed: ncurses, libsdl, curl, and in some distributions also ncurses-dev, libsdl-dev, curl-dev (package names may vary).

  • Run ./configure with the desired options. Suggested flags:

    --target-list=x86_64-softmmu --enable-virtfs --enable-gtk --extra-cflags='-Wno-discarded-qualifiers -Wno-unused-but-set-variable'
    
  • Change into the build directory:

    cd build
    
  • Run make (or ninja if installed).

  • Install with make install or run the binary directly (build/qemu-system-x86_64).

To emulate Tapedev:

  • Pass the option -device {"driver":"tapedev", ...} to QEMU. Repeat it to emulate multiple devices.

The device takes arguments formatted as JSON, for example: -device '{"driver":"tapedev","data_dir":"data","tape_types":[0,1,2,3,4],"tapes":[50,40,30,20,10]}'

  • data_dir - directory on the host where tape data will be stored

  • tape_types - array of tape type (0-4) of each section

  • tapes - array of numbers of tapes for each section

  • fast_mode - if set to false, the device will take more time to execute commands

To add the Tapedev device live (while QEMU is running):

  • Make sure you passed -qmp tcp:localhost:4444,server,wait=off as an argument when starting qemu

  • Open a netcat connection to the QMP socket: nc -v localhost 4444

  • Paste { "execute": "qmp_capabilities" }

  • Paste {"execute": "device_add", "arguments": {"driver": "tapedev", "data_dir": "data", "tape_types": [0,1,2,3,4], "tapes": [50,40,30,20,10] } }

  • Run echo 1 > /sys/bus/pci/rescan to detect the device in Linux.

To simulate device removal:

  • Run: echo 1 > /sys/bus/pci/devices/0000:<device_id>/remove