code:
==============
Data Integrity
==============

1. Introduction
===============

Modern filesystems feature checksumming of data and metadata to
protect against data corruption.  However, the detection of the
corruption is done at read time which could potentially be months
after the data was written.  At that point the original data that the
application tried to write is most likely lost.

The solution is to ensure that the disk is actually storing what the
application meant it to.  Recent additions to both the SCSI family
protocols (SBC Data Integrity Field, SCC protection proposal) as well
as SATA/T13 (External Path Protection) try to remedy this by adding
support for appending integrity metadata to an I/O.  The integrity
metadata (or protection information in SCSI terminology) includes a
checksum for each sector as well as an incrementing counter that
ensures the individual sectors are written in the right order.  And
for some protection schemes also that the I/O is written to the right
place on disk.

Current storage controllers and devices implement various protective
measures, for instance checksumming and scrubbing.  But these
technologies are working in their own isolated domains or at best
between adjacent nodes in the I/O path.  The interesting thing about
DIF and the other integrity extensions is that the protection format
is well defined and every node in the I/O path can verify the
integrity of the I/O and reject it if corruption is detected.  This
allows not only corruption prevention but also isolation of the point
of failure.

2. The Data Integrity Extensions
================================

As written, the protocol extensions only protect the path between
controller and storage device.  However, many controllers actually
allow the operating system to interact with the integrity metadata
(IMD).  We have been working with several FC/SAS HBA vendors to enable
the protection information to be transferred to and from their
controllers.

The SCSI Data Integrity Field works by appending 8 bytes of protection
information to each sector.  The data + integrity metadata is stored
in 520 byte sectors on disk.  Data + IMD are interleaved when
transferred between the controller and target.  The T13 proposal is
similar.

Because it is highly inconvenient for operating systems to deal with
520 (and 4104) byte sectors, we approached several HBA vendors and
encouraged them to allow separation of the data and integrity metadata
scatter-gather lists.

The controller will interleave the buffers on write and split them on
read.  This means that Linux can DMA the data buffers to and from
host memory without changes to the page cache.

Also, the 16-bit CRC checksum mandated by both the SCSI and SATA specs
is somewhat heavy to compute in software.  Benchmarks found that
calculating this checksum had a significant impact on system
performance for a number of workloads.  Some controllers allow a
lighter-weight checksum to be used when interfacing with the operating
system.  Emulex, for instance, supports the TCP/IP checksum instead.
The IP checksum received from the OS is converted to the 16-bit CRC
when writing and vice versa.  This allows the integrity metadata to be
generated by Linux or the application at very low cost (comparable to
software RAID5).

The IP checksum is weaker than the CRC in terms of detecting bit
errors.  However, the strength is really in the separation of the data
buffers and the integrity metadata.  These two distinct buffers must
match up for an I/O to complete.

The separation of the data and integrity metadata buffers as well as
the choice in checksums is referred to as the Data Integrity
Extensions.  As these extensions are outside the scope of the protocol
bodies (T10, T13), Oracle and its partners are trying to standardize
them within the Storage Networking Industry Association.

3. Kernel Changes
=================

The data integrity framework in Linux enables protection information
to be pinned to I/Os and sent to/received from controllers that
support it.

The advantage to the integrity extensions in SCSI and SATA is that
they enable us to protect the entire path from application to storage
device.  However, at the same time this is also the biggest
disadvantage. It means that the protection information must be in a
format that can be understood by the disk.

Generally Linux/POSIX applications are agnostic to the intricacies of
the storage devices they are accessing.  The virtual filesystem switch
and the block layer make things like hardware sector size and
transport protocols completely transparent to the application.

However, this level of detail is required when preparing the
protection information to send to a disk.  Consequently, the very
concept of an end-to-end protection scheme is a layering violation.
It is completely unreasonable for an application to be aware whether
it is accessing a SCSI or SATA disk.

The data integrity support implemented in Linux attempts to hide this
from the application.  As far as the application (and to some extent
the kernel) is concerned, the integrity metadata is opaque information
that's attached to the I/O.

The current implementation allows the block layer to automatically
generate the protection information for any I/O.  Eventually the
intent is to move the integrity metadata calculation to userspace for
user data.  Metadata and other I/O that originates within the kernel
will still use the automatic generation interface.

Some storage devices allow each hardware sector to be tagged with a
16-bit value.  The owner of this tag space is the owner of the block
device.  I.e. the filesystem in most cases.  The filesystem can use
this extra space to tag sectors as they see fit.  Because the tag
space is limited, the block interface allows tagging bigger chunks by
way of interleaving.  This way, 8*16 bits of information can be
attached to a typical 4KB filesystem block.

This also means that applications such as fsck and mkfs will need
access to manipulate the tags from user space.  A passthrough
interface for this is being worked on.

4. Block Layer Implementation Details
=====================================

4.1 Bio
-------

The data integrity patches add a new field to struct bio when
CONFIG_BLK_DEV_INTEGRITY is enabled.  bio_integrity(bio) returns a
pointer to a struct bip which contains the bio integrity payload.
Essentially a bip is a trimmed down struct bio which holds a bio_vec
containing the integrity metadata and the required housekeeping
information (bvec pool, vector count, etc.)

A kernel subsystem can enable data integrity protection on a bio by
calling bio_integrity_alloc(bio).  This will allocate and attach the
bip to the bio.

Individual pages containing integrity metadata can subsequently be
attached using bio_integrity_add_page().

bio_free() will automatically free the bip.

4.2 Block Device
----------------

Because the format of the protection data is tied to the physical
disk, each block device has been extended with a block integrity
profile (struct blk_integrity).  This optional profile is registered
with the block layer using blk_integrity_register().

The profile contains callback functions for generating and verifying
the protection data, as well as getting and setting application tags.
The profile also contains a few constants to aid in completing,
merging and splitting the integrity metadata.

Layered block devices will need to pick a profile that's appropriate
for all subdevices.  blk_integrity_compare() can help with that.  DM
and MD linear, RAID0 and RAID1 are currently supported.  RAID4/5/6
will require extra work due to the application tag.

5.0 Block Layer Integrity API
=============================

5.1 Normal Filesystem
---------------------

    The normal filesystem is unaware that the underlying block device
    is capable of sending/receiving integrity metadata.  The IMD will
    be automatically generated by the block layer at submit_bio() time
    in case of a WRITE.  A READ request will cause the I/O integrity
    to be verified upon completion.

    IMD generation and verification can be toggled using the::

      /sys/block/<bdev>/integrity/write_generate

    and::

      /sys/block/<bdev>/integrity/read_verify

    flags.

5.2 Integrity-Aware Filesystem
------------------------------

    A filesystem that is integrity-aware can prepare I/Os with IMD
    attached.  It can also use the application tag space if this is
    supported by the block device.

    `bool bio_integrity_prep(bio);`

      To generate IMD for WRITE and to set up buffers for READ, the
      filesystem must call bio_integrity_prep(bio).

      Prior to calling this function, the bio data direction and start
      sector must be set, and the bio should have all data pages
      added.  It is up to the caller to ensure that the bio does not
      change while I/O is in progress.
      Complete bio with error if prepare failed for some reson.

5.3 Passing Existing Integrity Metadata
---------------------------------------

    Filesystems that either generate their own integrity metadata or
    are capable of transferring IMD from user space can use the
    following calls:

    `struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages);`

      Allocates the bio integrity payload and hangs it off of the bio.
      nr_pages indicate how many pages of protection data need to be
      stored in the integrity bio_vec list (similar to bio_alloc()).

      The integrity payload will be freed at bio_free() time.

    `int bio_integrity_add_page(bio, page, len, offset);`

      Attaches a page containing integrity metadata to an existing
      bio.  The bio must have an existing bip,
      i.e. bio_integrity_alloc() must have been called.  For a WRITE,
      the integrity metadata in the pages must be in a format
      understood by the target device with the notable exception that
      the sector numbers will be remapped as the request traverses the
      I/O stack.  This implies that the pages added using this call
      will be modified during I/O!  The first reference tag in the
      integrity metadata must have a value of bip->bip_sector.

      Pages can be added using bio_integrity_add_page() as long as
      there is room in the bip bio_vec array (nr_pages).

      Upon completion of a READ operation, the attached pages will
      contain the integrity metadata received from the storage device.
      It is up to the receiver to process them and verify data
      integrity upon completion.

5.4 Registering A Block Device As Capable Of Exchanging Integrity Metadata
--------------------------------------------------------------------------

    To enable integrity exchange on a block device the gendisk must be
    registered as capable:

    `int blk_integrity_register(gendisk, blk_integrity);`

      The blk_integrity struct is a template and should contain the
      following::

        static struct blk_integrity my_profile = {
            .name                   = "STANDARDSBODY-TYPE-VARIANT-CSUM",
            .generate_fn            = my_generate_fn,
	    .verify_fn              = my_verify_fn,
	    .tuple_size             = sizeof(struct my_tuple_size),
	    .tag_size               = <tag bytes per hw sector>,
        };

      'name' is a text string which will be visible in sysfs.  This is
      part of the userland API so chose it carefully and never change
      it.  The format is standards body-type-variant.
      E.g. T10-DIF-TYPE1-IP or T13-EPP-0-CRC.

      'generate_fn' generates appropriate integrity metadata (for WRITE).

      'verify_fn' verifies that the data buffer matches the integrity
      metadata.

      'tuple_size' must be set to match the size of the integrity
      metadata per sector.  I.e. 8 for DIF and EPP.

      'tag_size' must be set to identify how many bytes of tag space
      are available per hardware sector.  For DIF this is either 2 or
      0 depending on the value of the Control Mode Page ATO bit.

----------------------------------------------------------------------

2007-12-24 Martin K. Petersen <martin.petersen@oracle.com>


code:
==============================
Deadline IO scheduler tunables
==============================

This little file attempts to document how the deadline io scheduler works.
In particular, it will clarify the meaning of the exposed tunables that may be
of interest to power users.

Selecting IO schedulers
-----------------------
Refer to Documentation/block/switching-sched.rst for information on
selecting an io scheduler on a per-device basis.

------------------------------------------------------------------------------

read_expire	(in ms)
-----------------------

The goal of the deadline io scheduler is to attempt to guarantee a start
service time for a request. As we focus mainly on read latencies, this is
tunable. When a read request first enters the io scheduler, it is assigned
a deadline that is the current time + the read_expire value in units of
milliseconds.

write_expire	(in ms)
-----------------------

Similar to read_expire mentioned above, but for writes.

fifo_batch	(number of requests)
------------------------------------

Requests are grouped into ``batches`` of a particular data direction (read or
write) which are serviced in increasing sector order.  To limit extra seeking,
deadline expiries are only checked between batches.  fifo_batch controls the
maximum number of requests per batch.

This parameter tunes the balance between per-request latency and aggregate
throughput.  When low latency is the primary concern, smaller is better (where
a value of 1 yields first-come first-served behaviour).  Increasing fifo_batch
generally improves throughput, at the cost of latency variation.

writes_starved	(number of dispatches)
--------------------------------------

When we have to move requests from the io scheduler queue to the block
device dispatch queue, we always give a preference to reads. However, we
don't want to starve writes indefinitely either. So writes_starved controls
how many times we give preference to reads over writes. When that has been
done writes_starved number of times, we dispatch some writes based on the
same criteria as reads.

front_merges	(bool)
----------------------

Sometimes it happens that a request enters the io scheduler that is contiguous
with a request that is already on the queue. Either it fits in the back of that
request, or it fits at the front. That is called either a back merge candidate
or a front merge candidate. Due to the way files are typically laid out,
back merges are much more common than front merges. For some work loads, you
may even know that it is a waste of time to spend any time attempting to
front merge requests. Setting front_merges to 0 disables this functionality.
Front merges may still occur due to the cached last_merge hint, but since
that comes at basically 0 cost we leave that on. We simply disable the
rbtree front sector lookup when the io scheduler merge function is called.

Nov 11 2002, Jens Axboe <jens.axboe@oracle.com>

repo_name: angery/linux


code:
.. SPDX-License-Identifier: GPL-2.0

=====
Block
=====

.. toctree::
   :maxdepth: 1

   bfq-iosched
   biovecs
   blk-mq
   capability
   cmdline-partition
   data-integrity
   deadline-iosched
   inline-encryption
   ioprio
   kyber-iosched
   null_blk
   pr
   request
   stat
   switching-sched
   writeback_cache_control
   ublk


code:
.. SPDX-License-Identifier: GPL-2.0

.. _inline_encryption:

=================
Inline Encryption
=================

Background
==========

Inline encryption hardware sits logically between memory and disk, and can
en/decrypt data as it goes in/out of the disk.  For each I/O request, software
can control exactly how the inline encryption hardware will en/decrypt the data
in terms of key, algorithm, data unit size (the granularity of en/decryption),
and data unit number (a value that determines the initialization vector(s)).

Some inline encryption hardware accepts all encryption parameters including raw
keys directly in low-level I/O requests.  However, most inline encryption
hardware instead has a fixed number of "keyslots" and requires that the key,
algorithm, and data unit size first be programmed into a keyslot.  Each
low-level I/O request then just contains a keyslot index and data unit number.

Note that inline encryption hardware is very different from traditional crypto
accelerators, which are supported through the kernel crypto API.  Traditional
crypto accelerators operate on memory regions, whereas inline encryption
hardware operates on I/O requests.  Thus, inline encryption hardware needs to be
managed by the block layer, not the kernel crypto API.

Inline encryption hardware is also very different from "self-encrypting drives",
such as those based on the TCG Opal or ATA Security standards.  Self-encrypting
drives don't provide fine-grained control of encryption and provide no way to
verify the correctness of the resulting ciphertext.  Inline encryption hardware
provides fine-grained control of encryption, including the choice of key and
initialization vector for each sector, and can be tested for correctness.

Objective
=========

We want to support inline encryption in the kernel.  To make testing easier, we
also want support for falling back to the kernel crypto API when actual inline
encryption hardware is absent.  We also want inline encryption to work with
layered devices like device-mapper and loopback (i.e. we want to be able to use
the inline encryption hardware of the underlying devices if present, or else
fall back to crypto API en/decryption).

Constraints and notes
=====================

- We need a way for upper layers (e.g. filesystems) to specify an encryption
  context to use for en/decrypting a bio, and device drivers (e.g. UFSHCD) need
  to be able to use that encryption context when they process the request.
  Encryption contexts also introduce constraints on bio merging; the block layer
  needs to be aware of these constraints.

- Different inline encryption hardware has different supported algorithms,
  supported data unit sizes, maximum data unit numbers, etc.  We call these
  properties the "crypto capabilities".  We need a way for device drivers to
  advertise crypto capabilities to upper layers in a generic way.

- Inline encryption hardware usually (but not always) requires that keys be
  programmed into keyslots before being used.  Since programming keyslots may be
  slow and there may not be very many keyslots, we shouldn't just program the
  key for every I/O request, but rather keep track of which keys are in the
  keyslots and reuse an already-programmed keyslot when possible.

- Upper layers typically define a specific end-of-life for crypto keys, e.g.
  when an encrypted directory is locked or when a crypto mapping is torn down.
  At these times, keys are wiped from memory.  We must provide a way for upper
  layers to also evict keys from any keyslots they are present in.

- When possible, device-mapper devices must be able to pass through the inline
  encryption support of their underlying devices.  However, it doesn't make
  sense for device-mapper devices to have keyslots themselves.

Basic design
============

We introduce ``struct blk_crypto_key`` to represent an inline encryption key and
how it will be used.  This includes the actual bytes of the key; the size of the
key; the algorithm and data unit size the key will be used with; and the number
of bytes needed to represent the maximum data unit number the key will be used
with.

We introduce ``struct bio_crypt_ctx`` to represent an encryption context.  It
contains a data unit number and a pointer to a blk_crypto_key.  We add pointers
to a bio_crypt_ctx to ``struct bio`` and ``struct request``; this allows users
of the block layer (e.g. filesystems) to provide an encryption context when
creating a bio and have it be passed down the stack for processing by the block
layer and device drivers.  Note that the encryption context doesn't explicitly
say whether to encrypt or decrypt, as that is implicit from the direction of the
bio; WRITE means encrypt, and READ means decrypt.

We also introduce ``struct blk_crypto_profile`` to contain all generic inline
encryption-related state for a particular inline encryption device.  The
blk_crypto_profile serves as the way that drivers for inline encryption hardware
advertise their crypto capabilities and provide certain functions (e.g.,
functions to program and evict keys) to upper layers.  Each device driver that
wants to support inline encryption will construct a blk_crypto_profile, then
associate it with the disk's request_queue.

The blk_crypto_profile also manages the hardware's keyslots, when applicable.
This happens in the block layer, so that users of the block layer can just
specify encryption contexts and don't need to know about keyslots at all, nor do
device drivers need to care about most details of keyslot management.

Specifically, for each keyslot, the block layer (via the blk_crypto_profile)
keeps track of which blk_crypto_key that keyslot contains (if any), and how many
in-flight I/O requests are using it.  When the block layer creates a
``struct request`` for a bio that has an encryption context, it grabs a keyslot
that already contains the key if possible.  Otherwise it waits for an idle
keyslot (a keyslot that isn't in-use by any I/O), then programs the key into the
least-recently-used idle keyslot using the function the device driver provided.
In both cases, the resulting keyslot is stored in the ``crypt_keyslot`` field of
the request, where it is then accessible to device drivers and is released after
the request completes.

``struct request`` also contains a pointer to the original bio_crypt_ctx.
Requests can be built from multiple bios, and the block layer must take the
encryption context into account when trying to merge bios and requests.  For two
bios/requests to be merged, they must have compatible encryption contexts: both
unencrypted, or both encrypted with the same key and contiguous data unit
numbers.  Only the encryption context for the first bio in a request is
retained, since the remaining bios have been verified to be merge-compatible
with the first bio.

To make it possible for inline encryption to work with request_queue based
layered devices, when a request is cloned, its encryption context is cloned as
well.  When the cloned request is submitted, it is then processed as usual; this
includes getting a keyslot from the clone's target device if needed.

blk-crypto-fallback
===================

It is desirable for the inline encryption support of upper layers (e.g.
filesystems) to be testable without real inline encryption hardware, and
likewise for the block layer's keyslot management logic.  It is also desirable
to allow upper layers to just always use inline encryption rather than have to
implement encryption in multiple ways.

Therefore, we also introduce *blk-crypto-fallback*, which is an implementation
of inline encryption using the kernel crypto API.  blk-crypto-fallback is built
into the block layer, so it works on any block device without any special setup.
Essentially, when a bio with an encryption context is submitted to a
block_device that doesn't support that encryption context, the block layer will
handle en/decryption of the bio using blk-crypto-fallback.

For encryption, the data cannot be encrypted in-place, as callers usually rely
on it being unmodified.  Instead, blk-crypto-fallback allocates bounce pages,
fills a new bio with those bounce pages, encrypts the data into those bounce
pages, and submits that "bounce" bio.  When the bounce bio completes,
blk-crypto-fallback completes the original bio.  If the original bio is too
large, multiple bounce bios may be required; see the code for details.

For decryption, blk-crypto-fallback "wraps" the bio's completion callback
(``bi_complete``) and private data (``bi_private``) with its own, unsets the
bio's encryption context, then submits the bio.  If the read completes
successfully, blk-crypto-fallback restores the bio's original completion
callback and private data, then decrypts the bio's data in-place using the
kernel crypto API.  Decryption happens from a workqueue, as it may sleep.
Afterwards, blk-crypto-fallback completes the bio.

In both cases, the bios that blk-crypto-fallback submits no longer have an
encryption context.  Therefore, lower layers only see standard unencrypted I/O.

blk-crypto-fallback also defines its own blk_crypto_profile and has its own
"keyslots"; its keyslots contain ``struct crypto_skcipher`` objects.  The reason
for this is twofold.  First, it allows the keyslot management logic to be tested
without actual inline encryption hardware.  Second, similar to actual inline
encryption hardware, the crypto API doesn't accept keys directly in requests but
rather requires that keys be set ahead of time, and setting keys can be
expensive; moreover, allocating a crypto_skcipher can't happen on the I/O path
at all due to the locks it takes.  Therefore, the concept of keyslots still
makes sense for blk-crypto-fallback.

Note that regardless of whether real inline encryption hardware or
blk-crypto-fallback is used, the ciphertext written to disk (and hence the
on-disk format of data) will be the same (assuming that both the inline
encryption hardware's implementation and the kernel crypto API's implementation
of the algorithm being used adhere to spec and function correctly).

blk-crypto-fallback is optional and is controlled by the
``CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK`` kernel configuration option.

API presented to users of the block layer
=========================================

``blk_crypto_config_supported()`` allows users to check ahead of time whether
inline encryption with particular crypto settings will work on a particular
block_device -- either via hardware or via blk-crypto-fallback.  This function
takes in a ``struct blk_crypto_config`` which is like blk_crypto_key, but omits
the actual bytes of the key and instead just contains the algorithm, data unit
size, etc.  This function can be useful if blk-crypto-fallback is disabled.

``blk_crypto_init_key()`` allows users to initialize a blk_crypto_key.

Users must call ``blk_crypto_start_using_key()`` before actually starting to use
a blk_crypto_key on a block_device (even if ``blk_crypto_config_supported()``
was called earlier).  This is needed to initialize blk-crypto-fallback if it
will be needed.  This must not be called from the data path, as this may have to
allocate resources, which may deadlock in that case.

Next, to attach an encryption context to a bio, users should call
``bio_crypt_set_ctx()``.  This function allocates a bio_crypt_ctx and attaches
it to a bio, given the blk_crypto_key and the data unit number that will be used
for en/decryption.  Users don't need to worry about freeing the bio_crypt_ctx
later, as that happens automatically when the bio is freed or reset.

Finally, when done using inline encryption with a blk_crypto_key on a
block_device, users must call ``blk_crypto_evict_key()``.  This ensures that
the key is evicted from all keyslots it may be programmed into and unlinked from
any kernel data structures it may be linked into.

In summary, for users of the block layer, the lifecycle of a blk_crypto_key is
as follows:

1. ``blk_crypto_config_supported()`` (optional)
2. ``blk_crypto_init_key()``
3. ``blk_crypto_start_using_key()``
4. ``bio_crypt_set_ctx()`` (potentially many times)
5. ``blk_crypto_evict_key()`` (after all I/O has completed)
6. Zeroize the blk_crypto_key (this has no dedicated function)

If a blk_crypto_key is being used on multiple block_devices, then
``blk_crypto_config_supported()`` (if used), ``blk_crypto_start_using_key()``,
and ``blk_crypto_evict_key()`` must be called on each block_device.

API presented to device drivers
===============================

A device driver that wants to support inline encryption must set up a
blk_crypto_profile in the request_queue of its device.  To do this, it first
must call ``blk_crypto_profile_init()`` (or its resource-managed variant
``devm_blk_crypto_profile_init()``), providing the number of keyslots.

Next, it must advertise its crypto capabilities by setting fields in the
blk_crypto_profile, e.g. ``modes_supported`` and ``max_dun_bytes_supported``.

It then must set function pointers in the ``ll_ops`` field of the
blk_crypto_profile to tell upper layers how to control the inline encryption
hardware, e.g. how to program and evict keyslots.  Most drivers will need to
implement ``keyslot_program`` and ``keyslot_evict``.  For details, see the
comments for ``struct blk_crypto_ll_ops``.

Once the driver registers a blk_crypto_profile with a request_queue, I/O
requests the driver receives via that queue may have an encryption context.  All
encryption contexts will be compatible with the crypto capabilities declared in
the blk_crypto_profile, so drivers don't need to worry about handling
unsupported requests.  Also, if a nonzero number of keyslots was declared in the
blk_crypto_profile, then all I/O requests that have an encryption context will
also have a keyslot which was already programmed with the appropriate key.

If the driver implements runtime suspend and its blk_crypto_ll_ops don't work
while the device is runtime-suspended, then the driver must also set the ``dev``
field of the blk_crypto_profile to point to the ``struct device`` that will be
resumed before any of the low-level operations are called.

If there are situations where the inline encryption hardware loses the contents
of its keyslots, e.g. device resets, the driver must handle reprogramming the
keyslots.  To do this, the driver may call ``blk_crypto_reprogram_all_keys()``.

Finally, if the driver used ``blk_crypto_profile_init()`` instead of
``devm_blk_crypto_profile_init()``, then it is responsible for calling
``blk_crypto_profile_destroy()`` when the crypto profile is no longer needed.

Layered Devices
===============

Request queue based layered devices like dm-rq that wish to support inline
encryption need to create their own blk_crypto_profile for their request_queue,
and expose whatever functionality they choose. When a layered device wants to
pass a clone of that request to another request_queue, blk-crypto will
initialize and prepare the clone as necessary; see
``blk_crypto_insert_cloned_request()``.

Interaction between inline encryption and blk integrity
=======================================================

At the time of this patch, there is no real hardware that supports both these
features. However, these features do interact with each other, and it's not
completely trivial to make them both work together properly. In particular,
when a WRITE bio wants to use inline encryption on a device that supports both
features, the bio will have an encryption context specified, after which
its integrity information is calculated (using the plaintext data, since
the encryption will happen while data is being written), and the data and
integrity info is sent to the device. Obviously, the integrity info must be
verified before the data is encrypted. After the data is encrypted, the device
must not store the integrity info that it received with the plaintext data
since that might reveal information about the plaintext data. As such, it must
re-generate the integrity info from the ciphertext data and store that on disk
instead. Another issue with storing the integrity info of the plaintext data is
that it changes the on disk format depending on whether hardware inline
encryption support is present or the kernel crypto API fallback is used (since
if the fallback is used, the device will receive the integrity info of the
ciphertext, not that of the plaintext).

Because there isn't any real hardware yet, it seems prudent to assume that
hardware implementations might not implement both features together correctly,
and disallow the combination for now. Whenever a device supports integrity, the
kernel will pretend that the device does not support hardware inline encryption
(by setting the blk_crypto_profile in the request_queue of the device to NULL).
When the crypto API fallback is enabled, this means that all bios with and
encryption context will use the fallback, and IO will complete as usual.  When
the fallback is disabled, a bio with an encryption context will be failed.


code:
===================
Block io priorities
===================

Intro
-----

With the introduction of cfq v3 (aka cfq-ts or time sliced cfq), basic io
priorities are supported for reads on files.  This enables users to io nice
processes or process groups, similar to what has been possible with cpu
scheduling for ages.  This document mainly details the current possibilities
with cfq; other io schedulers do not support io priorities thus far.

Scheduling classes
------------------

CFQ implements three generic scheduling classes that determine how io is
served for a process.

IOPRIO_CLASS_RT: This is the realtime io class. This scheduling class is given
higher priority than any other in the system, processes from this class are
given first access to the disk every time. Thus it needs to be used with some
care, one io RT process can starve the entire system. Within the RT class,
there are 8 levels of class data that determine exactly how much time this
process needs the disk for on each service. In the future this might change
to be more directly mappable to performance, by passing in a wanted data
rate instead.

IOPRIO_CLASS_BE: This is the best-effort scheduling class, which is the default
for any process that hasn't set a specific io priority. The class data
determines how much io bandwidth the process will get, it's directly mappable
to the cpu nice levels just more coarsely implemented. 0 is the highest
BE prio level, 7 is the lowest. The mapping between cpu nice level and io
nice level is determined as: io_nice = (cpu_nice + 20) / 5.

IOPRIO_CLASS_IDLE: This is the idle scheduling class, processes running at this
level only get io time when no one else needs the disk. The idle class has no
class data, since it doesn't really apply here.

Tools
-----

See below for a sample ionice tool. Usage::

	# ionice -c<class> -n<level> -p<pid>

If pid isn't given, the current process is assumed. IO priority settings
are inherited on fork, so you can use ionice to start the process at a given
level::

	# ionice -c2 -n0 /bin/ls

will run ls at the best-effort scheduling class at the highest priority.
For a running process, you can give the pid instead::

	# ionice -c1 -n2 -p100

will change pid 100 to run at the realtime scheduling class, at priority 2.

ionice.c tool::

  #include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <getopt.h>
  #include <unistd.h>
  #include <sys/ptrace.h>
  #include <asm/unistd.h>

  extern int sys_ioprio_set(int, int, int);
  extern int sys_ioprio_get(int, int);

  #if defined(__i386__)
  #define __NR_ioprio_set		289
  #define __NR_ioprio_get		290
  #elif defined(__ppc__)
  #define __NR_ioprio_set		273
  #define __NR_ioprio_get		274
  #elif defined(__x86_64__)
  #define __NR_ioprio_set		251
  #define __NR_ioprio_get		252
  #elif defined(__ia64__)
  #define __NR_ioprio_set		1274
  #define __NR_ioprio_get		1275
  #else
  #error "Unsupported arch"
  #endif

  static inline int ioprio_set(int which, int who, int ioprio)
  {
	return syscall(__NR_ioprio_set, which, who, ioprio);
  }

  static inline int ioprio_get(int which, int who)
  {
	return syscall(__NR_ioprio_get, which, who);
  }

  enum {
	IOPRIO_CLASS_NONE,
	IOPRIO_CLASS_RT,
	IOPRIO_CLASS_BE,
	IOPRIO_CLASS_IDLE,
  };

  enum {
	IOPRIO_WHO_PROCESS = 1,
	IOPRIO_WHO_PGRP,
	IOPRIO_WHO_USER,
  };

  #define IOPRIO_CLASS_SHIFT	13

  const char *to_prio[] = { "none", "realtime", "best-effort", "idle", };

  int main(int argc, char *argv[])
  {
	int ioprio = 4, set = 0, ioprio_class = IOPRIO_CLASS_BE;
	int c, pid = 0;

	while ((c = getopt(argc, argv, "+n:c:p:")) != EOF) {
		switch (c) {
		case 'n':
			ioprio = strtol(optarg, NULL, 10);
			set = 1;
			break;
		case 'c':
			ioprio_class = strtol(optarg, NULL, 10);
			set = 1;
			break;
		case 'p':
			pid = strtol(optarg, NULL, 10);
			break;
		}
	}

	switch (ioprio_class) {
		case IOPRIO_CLASS_NONE:
			ioprio_class = IOPRIO_CLASS_BE;
			break;
		case IOPRIO_CLASS_RT:
		case IOPRIO_CLASS_BE:
			break;
		case IOPRIO_CLASS_IDLE:
			ioprio = 7;
			break;
		default:
			printf("bad prio class %d\n", ioprio_class);
			return 1;
	}

	if (!set) {
		if (!pid && argv[optind])
			pid = strtol(argv[optind], NULL, 10);

		ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);

		printf("pid=%d, %d\n", pid, ioprio);

		if (ioprio == -1)
			perror("ioprio_get");
		else {
			ioprio_class = ioprio >> IOPRIO_CLASS_SHIFT;
			ioprio = ioprio & 0xff;
			printf("%s: prio %d\n", to_prio[ioprio_class], ioprio);
		}
	} else {
		if (ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio | ioprio_class << IOPRIO_CLASS_SHIFT) == -1) {
			perror("ioprio_set");
			return 1;
		}

		if (argv[optind])
			execvp(argv[optind], &argv[optind]);
	}

	return 0;
  }

March 11 2005, Jens Axboe <jens.axboe@oracle.com>


code:
============================
Kyber I/O scheduler tunables
============================

The only two tunables for the Kyber scheduler are the target latencies for
reads and synchronous writes. Kyber will throttle requests in order to meet
these target latencies.

read_lat_nsec
-------------
Target latency for reads (in nanoseconds).

write_lat_nsec
--------------
Target latency for synchronous writes (in nanoseconds).


code:
.. SPDX-License-Identifier: GPL-2.0

========================
Null block device driver
========================

Overview
========

The null block device (``/dev/nullb*``) is used for benchmarking the various
block-layer implementations. It emulates a block device of X gigabytes in size.
It does not execute any read/write operation, just mark them as complete in
the request queue. The following instances are possible:

  Multi-queue block-layer

    - Request-based.
    - Configurable submission queues per device.

  No block-layer (Known as bio-based)

    - Bio-based. IO requests are submitted directly to the device driver.
    - Directly accepts bio data structure and returns them.

All of them have a completion queue for each core in the system.

Module parameters
=================

queue_mode=[0-2]: Default: 2-Multi-queue
  Selects which block-layer the module should instantiate with.

  =  ============
  0  Bio-based
  1  Single-queue (deprecated)
  2  Multi-queue
  =  ============

home_node=[0--nr_nodes]: Default: NUMA_NO_NODE
  Selects what CPU node the data structures are allocated from.

gb=[Size in GB]: Default: 250GB
  The size of the device reported to the system.

bs=[Block size (in bytes)]: Default: 512 bytes
  The block size reported to the system.

nr_devices=[Number of devices]: Default: 1
  Number of block devices instantiated. They are instantiated as /dev/nullb0,
  etc.

irqmode=[0-2]: Default: 1-Soft-irq
  The completion mode used for completing IOs to the block-layer.

  =  ===========================================================================
  0  None.
  1  Soft-irq. Uses IPI to complete IOs across CPU nodes. Simulates the overhead
     when IOs are issued from another CPU node than the home the device is
     connected to.
  2  Timer: Waits a specific period (completion_nsec) for each IO before
     completion.
  =  ===========================================================================

completion_nsec=[ns]: Default: 10,000ns
  Combined with irqmode=2 (timer). The time each completion event must wait.

submit_queues=[1..nr_cpus]: Default: 1
  The number of submission queues attached to the device driver. If unset, it
  defaults to 1. For multi-queue, it is ignored when use_per_node_hctx module
  parameter is 1.

hw_queue_depth=[0..qdepth]: Default: 64
  The hardware queue depth of the device.

memory_backed=[0/1]: Default: 0
  Whether or not to use a memory buffer to respond to IO requests

  =  =============================================
  0  Transfer no data in response to IO requests
  1  Use a memory buffer to respond to IO requests
  =  =============================================

discard=[0/1]: Default: 0
  Support discard operations (requires memory-backed null_blk device).

  =  =====================================
  0  Do not support discard operations
  1  Enable support for discard operations
  =  =====================================

cache_size=[Size in MB]: Default: 0
  Cache size in MB for memory-backed device.

mbps=[Maximum bandwidth in MB/s]: Default: 0 (no limit)
  Bandwidth limit for device performance.

Multi-queue specific parameters
-------------------------------

use_per_node_hctx=[0/1]: Default: 0
  Number of hardware context queues.

  =  =====================================================================
  0  The number of submit queues are set to the value of the submit_queues
     parameter.
  1  The multi-queue block layer is instantiated with a hardware dispatch
     queue for each CPU node in the system.
  =  =====================================================================

no_sched=[0/1]: Default: 0
  Enable/disable the io scheduler.

  =  ======================================
  0  nullb* use default blk-mq io scheduler
  1  nullb* doesn't use io scheduler
  =  ======================================

blocking=[0/1]: Default: 0
  Blocking behavior of the request queue.

  =  ===============================================================
  0  Register as a non-blocking blk-mq driver device.
  1  Register as a blocking blk-mq driver device, null_blk will set
     the BLK_MQ_F_BLOCKING flag, indicating that it sometimes/always
     needs to block in its ->queue_rq() function.
  =  ===============================================================

shared_tags=[0/1]: Default: 0
  Sharing tags between devices.

  =  ================================================================
  0  Tag set is not shared.
  1  Tag set shared between devices for blk-mq. Only makes sense with
     nr_devices > 1, otherwise there's no tag set to share.
  =  ================================================================

zoned=[0/1]: Default: 0
  Device is a random-access or a zoned block device.

  =  ======================================================================
  0  Block device is exposed as a random-access block device.
  1  Block device is exposed as a host-managed zoned block device. Requires
     CONFIG_BLK_DEV_ZONED.
  =  ======================================================================

zone_size=[MB]: Default: 256
  Per zone size when exposed as a zoned block device. Must be a power of two.

zone_nr_conv=[nr_conv]: Default: 0
  The number of conventional zones to create when block device is zoned.  If
  zone_nr_conv >= nr_zones, it will be reduced to nr_zones - 1.


code:
===============================================
Block layer support for Persistent Reservations
===============================================

The Linux kernel supports a user space interface for simplified
Persistent Reservations which map to block devices that support
these (like SCSI). Persistent Reservations allow restricting
access to block devices to specific initiators in a shared storage
setup.

This document gives a general overview of the support ioctl commands.
For a more detailed reference please refer to the SCSI Primary
Commands standard, specifically the section on Reservations and the
"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands.

All implementations are expected to ensure the reservations survive
a power loss and cover all connections in a multi path environment.
These behaviors are optional in SPC but will be automatically applied
by Linux.

The following types of reservations are supported:
--------------------------------------------------

 - PR_WRITE_EXCLUSIVE
	Only the initiator that owns the reservation can write to the
	device.  Any initiator can read from the device.

 - PR_EXCLUSIVE_ACCESS
	Only the initiator that owns the reservation can access the
	device.

 - PR_WRITE_EXCLUSIVE_REG_ONLY
	Only initiators with a registered key can write to the device,
	Any initiator can read from the device.

 - PR_EXCLUSIVE_ACCESS_REG_ONLY
	Only initiators with a registered key can access the device.

 - PR_WRITE_EXCLUSIVE_ALL_REGS

	Only initiators with a registered key can write to the device,
	Any initiator can read from the device.
	All initiators with a registered key are considered reservation
	holders.
	Please reference the SPC spec on the meaning of a reservation
	holder if you want to use this type.

 - PR_EXCLUSIVE_ACCESS_ALL_REGS
	Only initiators with a registered key can access the device.
	All initiators with a registered key are considered reservation
	holders.
	Please reference the SPC spec on the meaning of a reservation
	holder if you want to use this type.

The following ioctl are supported:
----------------------------------

1. IOC_PR_REGISTER
^^^^^^^^^^^^^^^^^^

This ioctl command registers a new reservation if the new_key argument
is non-null.  If no existing reservation exists old_key must be zero,
if an existing reservation should be replaced old_key must contain
the old reservation key.

If the new_key argument is 0 it unregisters the existing reservation passed
in old_key.

2. IOC_PR_RESERVE
^^^^^^^^^^^^^^^^^

This ioctl command reserves the device and thus restricts access for other
devices based on the type argument.  The key argument must be the existing
reservation key for the device as acquired by the IOC_PR_REGISTER,
IOC_PR_REGISTER_IGNORE, IOC_PR_PREEMPT or IOC_PR_PREEMPT_ABORT commands.

3. IOC_PR_RELEASE
^^^^^^^^^^^^^^^^^

This ioctl command releases the reservation specified by key and flags
and thus removes any access restriction implied by it.

4. IOC_PR_PREEMPT
^^^^^^^^^^^^^^^^^

This ioctl command releases the existing reservation referred to by
old_key and replaces it with a new reservation of type for the
reservation key new_key.

5. IOC_PR_PREEMPT_ABORT
^^^^^^^^^^^^^^^^^^^^^^^

This ioctl command works like IOC_PR_PREEMPT except that it also aborts
any outstanding command sent over a connection identified by old_key.

6. IOC_PR_CLEAR
^^^^^^^^^^^^^^^

This ioctl command unregisters both key and any other reservation key
registered with the device and drops any existing reservation.

Flags
-----

All the ioctls have a flag field.  Currently only one flag is supported:

 - PR_FL_IGNORE_KEY
	Ignore the existing reservation key.  This is commonly supported for
	IOC_PR_REGISTER, and some implementation may support the flag for
	IOC_PR_RESERVE.

For all unknown flags the kernel will return -EOPNOTSUPP.


code:
============================
struct request documentation
============================

Jens Axboe <jens.axboe@oracle.com> 27/05/02

.. FIXME:
   No idea about what does mean - seems just some noise, so comment it

   1.0
   Index

   2.0 Struct request members classification

       2.1 struct request members explanation

   3.0

   2.0

Short explanation of request members
====================================

Classification flags:

	=	====================
	D	driver member
	B	block layer member
	I	I/O scheduler member
	=	====================

Unless an entry contains a D classification, a device driver must not access
this member. Some members may contain D classifications, but should only be
access through certain macros or functions (eg ->flags).

<linux/blkdev.h>

=============================== ======= =======================================
Member				Flag	Comment
=============================== ======= =======================================
struct list_head queuelist	BI	Organization on various internal
					queues

``void *elevator_private``	I	I/O scheduler private data

unsigned char cmd[16]		D	Driver can use this for setting up
					a cdb before execution, see
					blk_queue_prep_rq

unsigned long flags		DBI	Contains info about data direction,
					request type, etc.

int rq_status			D	Request status bits

kdev_t rq_dev			DBI	Target device

int errors			DB	Error counts

sector_t sector			DBI	Target location

unsigned long hard_nr_sectors	B	Used to keep sector sane

unsigned long nr_sectors	DBI	Total number of sectors in request

unsigned long hard_nr_sectors	B	Used to keep nr_sectors sane

unsigned short nr_phys_segments	DB	Number of physical scatter gather
					segments in a request

unsigned short nr_hw_segments	DB	Number of hardware scatter gather
					segments in a request

unsigned int current_nr_sectors	DB	Number of sectors in first segment
					of request

unsigned int hard_cur_sectors	B	Used to keep current_nr_sectors sane

int tag				DB	TCQ tag, if assigned

``void *special``		D	Free to be used by driver

``char *buffer``		D	Map of first segment, also see
					section on bouncing SECTION

``struct completion *waiting``	D	Can be used by driver to get signalled
					on request completion

``struct bio *bio``		DBI	First bio in request

``struct bio *biotail``		DBI	Last bio in request

``struct request_queue *q``	DB	Request queue this request belongs to

``struct request_list *rl``	B	Request list this request came from
=============================== ======= =======================================


code:
===============================================
Block layer statistics in /sys/block/<dev>/stat
===============================================

This file documents the contents of the /sys/block/<dev>/stat file.

The stat file provides several statistics about the state of block
device <dev>.

Q.
   Why are there multiple statistics in a single file?  Doesn't sysfs
   normally contain a single value per file?

A.
   By having a single file, the kernel can guarantee that the statistics
   represent a consistent snapshot of the state of the device.  If the
   statistics were exported as multiple files containing one statistic
   each, it would be impossible to guarantee that a set of readings
   represent a single point in time.

The stat file consists of a single line of text containing 17 decimal
values separated by whitespace.  The fields are summarized in the
following table, and described in more detail below.

=============== ============= =================================================
Name            units         description
=============== ============= =================================================
read I/Os       requests      number of read I/Os processed
read merges     requests      number of read I/Os merged with in-queue I/O
read sectors    sectors       number of sectors read
read ticks      milliseconds  total wait time for read requests
write I/Os      requests      number of write I/Os processed
write merges    requests      number of write I/Os merged with in-queue I/O
write sectors   sectors       number of sectors written
write ticks     milliseconds  total wait time for write requests
in_flight       requests      number of I/Os currently in flight
io_ticks        milliseconds  total time this block device has been active
time_in_queue   milliseconds  total wait time for all requests
discard I/Os    requests      number of discard I/Os processed
discard merges  requests      number of discard I/Os merged with in-queue I/O
discard sectors sectors       number of sectors discarded
discard ticks   milliseconds  total wait time for discard requests
flush I/Os      requests      number of flush I/Os processed
flush ticks     milliseconds  total wait time for flush requests
=============== ============= =================================================

read I/Os, write I/Os, discard I/0s
===================================

These values increment when an I/O request completes.

flush I/Os
==========

These values increment when an flush I/O request completes.

Block layer combines flush requests and executes at most one at a time.
This counts flush requests executed by disk. Not tracked for partitions.

read merges, write merges, discard merges
=========================================

These values increment when an I/O request is merged with an
already-queued I/O request.

read sectors, write sectors, discard_sectors
============================================

These values count the number of sectors read from, written to, or
discarded from this block device.  The "sectors" in question are the
standard UNIX 512-byte sectors, not any device- or filesystem-specific
block size.  The counters are incremented when the I/O completes.

read ticks, write ticks, discard ticks, flush ticks
===================================================

These values count the number of milliseconds that I/O requests have
waited on this block device.  If there are multiple I/O requests waiting,
these values will increase at a rate greater than 1000/second; for
example, if 60 read requests wait for an average of 30 ms, the read_ticks
field will increase by 60*30 = 1800.

in_flight
=========

This value counts the number of I/O requests that have been issued to
the device driver but have not yet completed.  It does not include I/O
requests that are in the queue but not yet issued to the device driver.

io_ticks
========

This value counts the number of milliseconds during which the device has
had I/O requests queued.

time_in_queue
=============

This value counts the number of milliseconds that I/O requests have waited
on this block device.  If there are multiple I/O requests waiting, this
value will increase as the product of the number of milliseconds times the
number of requests waiting (see "read ticks" above for an example).


code:
===================
Switching Scheduler
===================

Each io queue has a set of io scheduler tunables associated with it. These
tunables control how the io scheduler works. You can find these entries
in::

	/sys/block/<device>/queue/iosched

assuming that you have sysfs mounted on /sys. If you don't have sysfs mounted,
you can do so by typing::

	# mount none /sys -t sysfs

It is possible to change the IO scheduler for a given block device on
the fly to select one of mq-deadline, none, bfq, or kyber schedulers -
which can improve that device's throughput.

To set a specific scheduler, simply do this::

	echo SCHEDNAME > /sys/block/DEV/queue/scheduler

where SCHEDNAME is the name of a defined IO scheduler, and DEV is the
device name (hda, hdb, sga, or whatever you happen to have).

The list of defined schedulers can be found by simply doing
a "cat /sys/block/DEV/queue/scheduler" - the list of valid names
will be displayed, with the currently selected scheduler in brackets::

  # cat /sys/block/sda/queue/scheduler
  [mq-deadline] kyber bfq none
  # echo none >/sys/block/sda/queue/scheduler
  # cat /sys/block/sda/queue/scheduler
  [none] mq-deadline kyber bfq


code:
.. SPDX-License-Identifier: GPL-2.0

===========================================
Userspace block device driver (ublk driver)
===========================================

Overview
========

ublk is a generic framework for implementing block device logic from userspace.
The motivation behind it is that moving virtual block drivers into userspace,
such as loop, nbd and similar can be very helpful. It can help to implement
new virtual block device such as ublk-qcow2 (there are several attempts of
implementing qcow2 driver in kernel).

Userspace block devices are attractive because:

- They can be written many programming languages.
- They can use libraries that are not available in the kernel.
- They can be debugged with tools familiar to application developers.
- Crashes do not kernel panic the machine.
- Bugs are likely to have a lower security impact than bugs in kernel
  code.
- They can be installed and updated independently of the kernel.
- They can be used to simulate block device easily with user specified
  parameters/setting for test/debug purpose

ublk block device (``/dev/ublkb*``) is added by ublk driver. Any IO request
on the device will be forwarded to ublk userspace program. For convenience,
in this document, ``ublk server`` refers to generic ublk userspace
program. ``ublksrv`` [#userspace]_ is one of such implementation. It
provides ``libublksrv`` [#userspace_lib]_ library for developing specific
user block device conveniently, while also generic type block device is
included, such as loop and null. Richard W.M. Jones wrote userspace nbd device
``nbdublk`` [#userspace_nbdublk]_  based on ``libublksrv`` [#userspace_lib]_.

After the IO is handled by userspace, the result is committed back to the
driver, thus completing the request cycle. This way, any specific IO handling
logic is totally done by userspace, such as loop's IO handling, NBD's IO
communication, or qcow2's IO mapping.

``/dev/ublkb*`` is driven by blk-mq request-based driver. Each request is
assigned by one queue wide unique tag. ublk server assigns unique tag to each
IO too, which is 1:1 mapped with IO of ``/dev/ublkb*``.

Both the IO request forward and IO handling result committing are done via
``io_uring`` passthrough command; that is why ublk is also one io_uring based
block driver. It has been observed that using io_uring passthrough command can
give better IOPS than block IO; which is why ublk is one of high performance
implementation of userspace block device: not only IO request communication is
done by io_uring, but also the preferred IO handling in ublk server is io_uring
based approach too.

ublk provides control interface to set/get ublk block device parameters.
The interface is extendable and kabi compatible: basically any ublk request
queue's parameter or ublk generic feature parameters can be set/get via the
interface. Thus, ublk is generic userspace block device framework.
For example, it is easy to setup a ublk device with specified block
parameters from userspace.

Using ublk
==========

ublk requires userspace ublk server to handle real block device logic.

Below is example of using ``ublksrv`` to provide ublk-based loop device.

- add a device::

     ublk add -t loop -f ublk-loop.img

- format with xfs, then use it::

     mkfs.xfs /dev/ublkb0
     mount /dev/ublkb0 /mnt
     # do anything. all IOs are handled by io_uring
     ...
     umount /mnt

- list the devices with their info::

     ublk list

- delete the device::

     ublk del -a
     ublk del -n $ublk_dev_id

See usage details in README of ``ublksrv`` [#userspace_readme]_.

Design
======

Control plane
-------------

ublk driver provides global misc device node (``/dev/ublk-control``) for
managing and controlling ublk devices with help of several control commands:

- ``UBLK_CMD_ADD_DEV``

  Add a ublk char device (``/dev/ublkc*``) which is talked with ublk server
  WRT IO command communication. Basic device info is sent together with this
  command. It sets UAPI structure of ``ublksrv_ctrl_dev_info``,
  such as ``nr_hw_queues``, ``queue_depth``, and max IO request buffer size,
  for which the info is negotiated with the driver and sent back to the server.
  When this command is completed, the basic device info is immutable.

- ``UBLK_CMD_SET_PARAMS`` / ``UBLK_CMD_GET_PARAMS``

  Set or get parameters of the device, which can be either generic feature
  related, or request queue limit related, but can't be IO logic specific,
  because the driver does not handle any IO logic. This command has to be
  sent before sending ``UBLK_CMD_START_DEV``.

- ``UBLK_CMD_START_DEV``

  After the server prepares userspace resources (such as creating per-queue
  pthread & io_uring for handling ublk IO), this command is sent to the
  driver for allocating & exposing ``/dev/ublkb*``. Parameters set via
  ``UBLK_CMD_SET_PARAMS`` are applied for creating the device.

- ``UBLK_CMD_STOP_DEV``

  Halt IO on ``/dev/ublkb*`` and remove the device. When this command returns,
  ublk server will release resources (such as destroying per-queue pthread &
  io_uring).

- ``UBLK_CMD_DEL_DEV``

  Remove ``/dev/ublkc*``. When this command returns, the allocated ublk device
  number can be reused.

- ``UBLK_CMD_GET_QUEUE_AFFINITY``

  When ``/dev/ublkc`` is added, the driver creates block layer tagset, so
  that each queue's affinity info is available. The server sends
  ``UBLK_CMD_GET_QUEUE_AFFINITY`` to retrieve queue affinity info. It can
  set up the per-queue context efficiently, such as bind affine CPUs with IO
  pthread and try to allocate buffers in IO thread context.

- ``UBLK_CMD_GET_DEV_INFO``

  For retrieving device info via ``ublksrv_ctrl_dev_info``. It is the server's
  responsibility to save IO target specific info in userspace.

- ``UBLK_CMD_START_USER_RECOVERY``

  This command is valid if ``UBLK_F_USER_RECOVERY`` feature is enabled. This
  command is accepted after the old process has exited, ublk device is quiesced
  and ``/dev/ublkc*`` is released. User should send this command before he starts
  a new process which re-opens ``/dev/ublkc*``. When this command returns, the
  ublk device is ready for the new process.

- ``UBLK_CMD_END_USER_RECOVERY``

  This command is valid if ``UBLK_F_USER_RECOVERY`` feature is enabled. This
  command is accepted after ublk device is quiesced and a new process has
  opened ``/dev/ublkc*`` and get all ublk queues be ready. When this command
  returns, ublk device is unquiesced and new I/O requests are passed to the
  new process.

- user recovery feature description

  Two new features are added for user recovery: ``UBLK_F_USER_RECOVERY`` and
  ``UBLK_F_USER_RECOVERY_REISSUE``.

  With ``UBLK_F_USER_RECOVERY`` set, after one ubq_daemon(ublk server's io
  handler) is dying, ublk does not delete ``/dev/ublkb*`` during the whole
  recovery stage and ublk device ID is kept. It is ublk server's
  responsibility to recover the device context by its own knowledge.
  Requests which have not been issued to userspace are requeued. Requests
  which have been issued to userspace are aborted.

  With ``UBLK_F_USER_RECOVERY_REISSUE`` set, after one ubq_daemon(ublk
  server's io handler) is dying, contrary to ``UBLK_F_USER_RECOVERY``,
  requests which have been issued to userspace are requeued and will be
  re-issued to the new process after handling ``UBLK_CMD_END_USER_RECOVERY``.
  ``UBLK_F_USER_RECOVERY_REISSUE`` is designed for backends who tolerate
  double-write since the driver may issue the same I/O request twice. It
  might be useful to a read-only FS or a VM backend.

Data plane
----------

ublk server needs to create per-queue IO pthread & io_uring for handling IO
commands via io_uring passthrough. The per-queue IO pthread
focuses on IO handling and shouldn't handle any control & management
tasks.

The's IO is assigned by a unique tag, which is 1:1 mapping with IO
request of ``/dev/ublkb*``.

UAPI structure of ``ublksrv_io_desc`` is defined for describing each IO from
the driver. A fixed mmaped area (array) on ``/dev/ublkc*`` is provided for
exporting IO info to the server; such as IO offset, length, OP/flags and
buffer address. Each ``ublksrv_io_desc`` instance can be indexed via queue id
and IO tag directly.

The following IO commands are communicated via io_uring passthrough command,
and each command is only for forwarding the IO and committing the result
with specified IO tag in the command data:

- ``UBLK_IO_FETCH_REQ``

  Sent from the server IO pthread for fetching future incoming IO requests
  destined to ``/dev/ublkb*``. This command is sent only once from the server
  IO pthread for ublk driver to setup IO forward environment.

- ``UBLK_IO_COMMIT_AND_FETCH_REQ``

  When an IO request is destined to ``/dev/ublkb*``, the driver stores
  the IO's ``ublksrv_io_desc`` to the specified mapped area; then the
  previous received IO command of this IO tag (either ``UBLK_IO_FETCH_REQ``
  or ``UBLK_IO_COMMIT_AND_FETCH_REQ)`` is completed, so the server gets
  the IO notification via io_uring.

  After the server handles the IO, its result is committed back to the
  driver by sending ``UBLK_IO_COMMIT_AND_FETCH_REQ`` back. Once ublkdrv
  received this command, it parses the result and complete the request to
  ``/dev/ublkb*``. In the meantime setup environment for fetching future
  requests with the same IO tag. That is, ``UBLK_IO_COMMIT_AND_FETCH_REQ``
  is reused for both fetching request and committing back IO result.

- ``UBLK_IO_NEED_GET_DATA``

  With ``UBLK_F_NEED_GET_DATA`` enabled, the WRITE request will be firstly
  issued to ublk server without data copy. Then, IO backend of ublk server
  receives the request and it can allocate data buffer and embed its addr
  inside this new io command. After the kernel driver gets the command,
  data copy is done from request pages to this backend's buffer. Finally,
  backend receives the request again with data to be written and it can
  truly handle the request.

  ``UBLK_IO_NEED_GET_DATA`` adds one additional round-trip and one
  io_uring_enter() syscall. Any user thinks that it may lower performance
  should not enable UBLK_F_NEED_GET_DATA. ublk server pre-allocates IO
  buffer for each IO by default. Any new project should try to use this
  buffer to communicate with ublk driver. However, existing project may
  break or not able to consume the new buffer interface; that's why this
  command is added for backwards compatibility so that existing projects
  can still consume existing buffers.

- data copy between ublk server IO buffer and ublk block IO request

  The driver needs to copy the block IO request pages into the server buffer
  (pages) first for WRITE before notifying the server of the coming IO, so
  that the server can handle WRITE request.

  When the server handles READ request and sends
  ``UBLK_IO_COMMIT_AND_FETCH_REQ`` to the server, ublkdrv needs to copy
  the server buffer (pages) read to the IO request pages.

Future development
==================

Container-aware ublk deivice
----------------------------

ublk driver doesn't handle any IO logic. Its function is well defined
for now and very limited userspace interfaces are needed, which is also
well defined too. It is possible to make ublk devices container-aware block
devices in future as Stefan Hajnoczi suggested [#stefan]_, by removing
ADMIN privilege.

Zero copy
---------

Zero copy is a generic requirement for nbd, fuse or similar drivers. A
problem [#xiaoguang]_ Xiaoguang mentioned is that pages mapped to userspace
can't be remapped any more in kernel with existing mm interfaces. This can
occurs when destining direct IO to ``/dev/ublkb*``. Also, he reported that
big requests (IO size >= 256 KB) may benefit a lot from zero copy.

References
==========

.. [#userspace] https://github.com/ming1/ubdsrv

.. [#userspace_lib] https://github.com/ming1/ubdsrv/tree/master/lib

.. [#userspace_nbdublk] https://gitlab.com/rwmjones/libnbd/-/tree/nbdublk

.. [#userspace_readme] https://github.com/ming1/ubdsrv/blob/master/README

.. [#stefan] https://lore.kernel.org/linux-block/YoOr6jBfgVm8GvWg@stefanha-x1.localdomain/

.. [#xiaoguang] https://lore.kernel.org/linux-block/YoOr6jBfgVm8GvWg@stefanha-x1.localdomain/


code:
==========================================
Explicit volatile write back cache control
==========================================

Introduction
------------

Many storage devices, especially in the consumer market, come with volatile
write back caches.  That means the devices signal I/O completion to the
operating system before data actually has hit the non-volatile storage.  This
behavior obviously speeds up various workloads, but it means the operating
system needs to force data out to the non-volatile storage when it performs
a data integrity operation like fsync, sync or an unmount.

The Linux block layer provides two simple mechanisms that let filesystems
control the caching behavior of the storage device.  These mechanisms are
a forced cache flush, and the Force Unit Access (FUA) flag for requests.

Explicit cache flushes
----------------------

The REQ_PREFLUSH flag can be OR ed into the r/w flags of a bio submitted from
the filesystem and will make sure the volatile cache of the storage device
has been flushed before the actual I/O operation is started.  This explicitly
guarantees that previously completed write requests are on non-volatile
storage before the flagged bio starts. In addition the REQ_PREFLUSH flag can be
set on an otherwise empty bio structure, which causes only an explicit cache
flush without any dependent I/O.  It is recommend to use
the blkdev_issue_flush() helper for a pure cache flush.

Forced Unit Access
------------------

The REQ_FUA flag can be OR ed into the r/w flags of a bio submitted from the
filesystem and will make sure that I/O completion for this request is only
signaled after the data has been committed to non-volatile storage.

Implementation details for filesystems
--------------------------------------

Filesystems can simply set the REQ_PREFLUSH and REQ_FUA bits and do not have to
worry if the underlying devices need any explicit cache flushing and how
the Forced Unit Access is implemented.  The REQ_PREFLUSH and REQ_FUA flags
may both be set on a single bio.

Implementation details for bio based block drivers
--------------------------------------------------------------

These drivers will always see the REQ_PREFLUSH and REQ_FUA bits as they sit
directly below the submit_bio interface.  For remapping drivers the REQ_FUA
bits need to be propagated to underlying devices, and a global flush needs
to be implemented for bios with the REQ_PREFLUSH bit set.  For real device
drivers that do not have a volatile cache the REQ_PREFLUSH and REQ_FUA bits
on non-empty bios can simply be ignored, and REQ_PREFLUSH requests without
data can be completed successfully without doing any work.  Drivers for
devices with volatile caches need to implement the support for these
flags themselves without any help from the block layer.

Implementation details for request_fn based block drivers
---------------------------------------------------------

For devices that do not support volatile write caches there is no driver
support required, the block layer completes empty REQ_PREFLUSH requests before
entering the driver and strips off the REQ_PREFLUSH and REQ_FUA bits from
requests that have a payload.  For devices with volatile write caches the
driver needs to tell the block layer that it supports flushing caches by
doing::

	blk_queue_write_cache(sdkp->disk->queue, true, false);

and handle empty REQ_OP_FLUSH requests in its prep_fn/request_fn.  Note that
REQ_PREFLUSH requests with a payload are automatically turned into a sequence
of an empty REQ_OP_FLUSH request followed by the actual write by the block
layer.  For devices that also support the FUA bit the block layer needs
to be told to pass through the REQ_FUA bit using::

	blk_queue_write_cache(sdkp->disk->queue, true, true);

and the driver must handle write requests that have the REQ_FUA bit set
in prep_fn/request_fn.  If the FUA bit is not natively supported the block
layer turns it into an empty REQ_OP_FLUSH request after the actual write.


code:
==============
BPF Design Q&A
==============

BPF extensibility and applicability to networking, tracing, security
in the linux kernel and several user space implementations of BPF
virtual machine led to a number of misunderstanding on what BPF actually is.
This short QA is an attempt to address that and outline a direction
of where BPF is heading long term.

.. contents::
    :local:
    :depth: 3

Questions and Answers
=====================

Q: Is BPF a generic instruction set similar to x64 and arm64?
-------------------------------------------------------------
A: NO.

Q: Is BPF a generic virtual machine ?
-------------------------------------
A: NO.

BPF is generic instruction set *with* C calling convention.
-----------------------------------------------------------

Q: Why C calling convention was chosen?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

A: Because BPF programs are designed to run in the linux kernel
which is written in C, hence BPF defines instruction set compatible
with two most used architectures x64 and arm64 (and takes into
consideration important quirks of other architectures) and
defines calling convention that is compatible with C calling
convention of the linux kernel on those architectures.

Q: Can multiple return values be supported in the future?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: NO. BPF allows only register R0 to be used as return value.

Q: Can more than 5 function arguments be supported in the future?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: NO. BPF calling convention only allows registers R1-R5 to be used
as arguments. BPF is not a standalone instruction set.
(unlike x64 ISA that allows msft, cdecl and other conventions)

Q: Can BPF programs access instruction pointer or return address?
-----------------------------------------------------------------
A: NO.

Q: Can BPF programs access stack pointer ?
------------------------------------------
A: NO.

Only frame pointer (register R10) is accessible.
From compiler point of view it's necessary to have stack pointer.
For example, LLVM defines register R11 as stack pointer in its
BPF backend, but it makes sure that generated code never uses it.

Q: Does C-calling convention diminishes possible use cases?
-----------------------------------------------------------
A: YES.

BPF design forces addition of major functionality in the form
of kernel helper functions and kernel objects like BPF maps with
seamless interoperability between them. It lets kernel call into
BPF programs and programs call kernel helpers with zero overhead,
as all of them were native C code. That is particularly the case
for JITed BPF programs that are indistinguishable from
native kernel C code.

Q: Does it mean that 'innovative' extensions to BPF code are disallowed?
------------------------------------------------------------------------
A: Soft yes.

At least for now, until BPF core has support for
bpf-to-bpf calls, indirect calls, loops, global variables,
jump tables, read-only sections, and all other normal constructs
that C code can produce.

Q: Can loops be supported in a safe way?
----------------------------------------
A: It's not clear yet.

BPF developers are trying to find a way to
support bounded loops.

Q: What are the verifier limits?
--------------------------------
A: The only limit known to the user space is BPF_MAXINSNS (4096).
It's the maximum number of instructions that the unprivileged bpf
program can have. The verifier has various internal limits.
Like the maximum number of instructions that can be explored during
program analysis. Currently, that limit is set to 1 million.
Which essentially means that the largest program can consist
of 1 million NOP instructions. There is a limit to the maximum number
of subsequent branches, a limit to the number of nested bpf-to-bpf
calls, a limit to the number of the verifier states per instruction,
a limit to the number of maps used by the program.
All these limits can be hit with a sufficiently complex program.
There are also non-numerical limits that can cause the program
to be rejected. The verifier used to recognize only pointer + constant
expressions. Now it can recognize pointer + bounded_register.
bpf_lookup_map_elem(key) had a requirement that 'key' must be
a pointer to the stack. Now, 'key' can be a pointer to map value.
The verifier is steadily getting 'smarter'. The limits are
being removed. The only way to know that the program is going to
be accepted by the verifier is to try to load it.
The bpf development process guarantees that the future kernel
versions will accept all bpf programs that were accepted by
the earlier versions.

Instruction level questions
---------------------------

Q: LD_ABS and LD_IND instructions vs C code
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Q: How come LD_ABS and LD_IND instruction are present in BPF whereas
C code cannot express them and has to use builtin intrinsics?

A: This is artifact of compatibility with classic BPF. Modern
networking code in BPF performs better without them.
See 'direct packet access'.

Q: BPF instructions mapping not one-to-one to native CPU
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Q: It seems not all BPF instructions are one-to-one to native CPU.
For example why BPF_JNE and other compare and jumps are not cpu-like?

A: This was necessary to avoid introducing flags into ISA which are
impossible to make generic and efficient across CPU architectures.

Q: Why BPF_DIV instruction doesn't map to x64 div?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because if we picked one-to-one relationship to x64 it would have made
it more complicated to support on arm64 and other archs. Also it
needs div-by-zero runtime check.

Q: Why there is no BPF_SDIV for signed divide operation?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because it would be rarely used. llvm errors in such case and
prints a suggestion to use unsigned divide instead.

Q: Why BPF has implicit prologue and epilogue?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because architectures like sparc have register windows and in general
there are enough subtle differences between architectures, so naive
store return address into stack won't work. Another reason is BPF has
to be safe from division by zero (and legacy exception path
of LD_ABS insn). Those instructions need to invoke epilogue and
return implicitly.

Q: Why BPF_JLT and BPF_JLE instructions were not introduced in the beginning?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A: Because classic BPF didn't have them and BPF authors felt that compiler
workaround would be acceptable. Turned out that programs lose performance
due to lack of these compare instructions and they were added.
These two instructions is a perfect example what kind of new BPF
instructions are acceptable and can be added in the future.
These two already had equivalent instructions in native CPUs.
New instructions that don't have one-to-one mapping to HW instructions
will not be accepted.

Q: BPF 32-bit subregister requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Q: BPF 32-bit subregisters have a requirement to zero upper 32-bits of BPF
registers which makes BPF inefficient virtual machine for 32-bit
CPU architectures and 32-bit HW accelerators. Can true 32-bit registers
be added to BPF in the future?

A: NO.

But some optimizations on zero-ing the upper 32 bits for BPF registers are
available, and can be leveraged to improve the performance of JITed BPF
programs for 32-bit architectures.

Starting with version 7, LLVM is able to generate instructions that operate
on 32-bit subregisters, provided the option -mattr=+alu32 is passed for
compiling a program. Furthermore, the verifier can now mark the
instructions for which zero-ing the upper bits of the destination register
is required, and insert an explicit zero-extension (zext) instruction
(a mov32 variant). This means that for architectures without zext hardware
support, the JIT back-ends do not need to clear the upper bits for
subregisters written by alu32 instructions or narrow loads. Instead, the
back-ends simply need to support code generation for that mov32 variant,
and to overwrite bpf_jit_needs_zext() to make it return "true" (in order to
enable zext insertion in the verifier).

Note that it is possible for a JIT back-end to have partial hardware
support for zext. In that case, if verifier zext insertion is enabled,
it could lead to the insertion of unnecessary zext instructions. Such
instructions could be removed by creating a simple peephole inside the JIT
back-end: if one instruction has hardware support for zext and if the next
instruction is an explicit zext, then the latter can be skipped when doing
the code generation.

Q: Does BPF have a stable ABI?
------------------------------
A: YES. BPF instructions, arguments to BPF programs, set of helper
functions and their arguments, recognized return codes are all part
of ABI. However there is one specific exception to tracing programs
which are using helpers like bpf_probe_read() to walk kernel internal
data structures and compile with kernel internal headers. Both of these
kernel internals are subject to change and can break with newer kernels
such that the program needs to be adapted accordingly.

Q: Are tracepoints part of the stable ABI?
------------------------------------------
A: NO. Tracepoints are tied to internal implementation details hence they are
subject to change and can break with newer kernels. BPF programs need to change
accordingly when this happens.

Q: Are places where kprobes can attach part of the stable ABI?
--------------------------------------------------------------
A: NO. The places to which kprobes can attach are internal implementation
details, which means that they are subject to change and can break with
newer kernels. BPF programs need to change accordingly when this happens.

Q: How much stack space a BPF program uses?
-------------------------------------------
A: Currently all program types are limited to 512 bytes of stack
space, but the verifier computes the actual amount of stack used
and both interpreter and most JITed code consume necessary amount.

Q: Can BPF be offloaded to HW?
------------------------------
A: YES. BPF HW offload is supported by NFP driver.

Q: Does classic BPF interpreter still exist?
--------------------------------------------
A: NO. Classic BPF programs are converted into extend BPF instructions.

Q: Can BPF call arbitrary kernel functions?
-------------------------------------------
A: NO. BPF programs can only call a set of helper functions which
is defined for every program type.

Q: Can BPF overwrite arbitrary kernel memory?
---------------------------------------------
A: NO.

Tracing bpf programs can *read* arbitrary memory with bpf_probe_read()
and bpf_probe_read_str() helpers. Networking programs cannot read
arbitrary memory, since they don't have access to these helpers.
Programs can never read or write arbitrary memory directly.

Q: Can BPF overwrite arbitrary user memory?
-------------------------------------------
A: Sort-of.

Tracing BPF programs can overwrite the user memory
of the current task with bpf_probe_write_user(). Every time such
program is loaded the kernel will print warning message, so
this helper is only useful for experiments and prototypes.
Tracing BPF programs are root only.

Q: New functionality via kernel modules?
----------------------------------------
Q: Can BPF functionality such as new program or map types, new
helpers, etc be added out of kernel module code?

A: NO.

Q: Directly calling kernel function is an ABI?
----------------------------------------------
Q: Some kernel functions (e.g. tcp_slow_start) can be called
by BPF programs.  Do these kernel functions become an ABI?

A: NO.

The kernel function protos will change and the bpf programs will be
rejected by the verifier.  Also, for example, some of the bpf-callable
kernel functions have already been used by other kernel tcp
cc (congestion-control) implementations.  If any of these kernel
functions has changed, both the in-tree and out-of-tree kernel tcp cc
implementations have to be changed.  The same goes for the bpf
programs and they have to be adjusted accordingly.

Q: Attaching to arbitrary kernel functions is an ABI?
-----------------------------------------------------
Q: BPF programs can be attached to many kernel functions.  Do these
kernel functions become part of the ABI?

A: NO.

The kernel function prototypes will change, and BPF programs attaching to
them will need to change.  The BPF compile-once-run-everywhere (CO-RE)
should be used in order to make it easier to adapt your BPF programs to
different versions of the kernel.

Q: Marking a function with BTF_ID makes that function an ABI?
-------------------------------------------------------------
A: NO.

The BTF_ID macro does not cause a function to become part of the ABI
any more than does the EXPORT_SYMBOL_GPL macro.

Q: What is the compatibility story for special BPF types in map values?
-----------------------------------------------------------------------
Q: Users are allowed to embed bpf_spin_lock, bpf_timer fields in their BPF map
values (when using BTF support for BPF maps). This allows to use helpers for
such objects on these fields inside map values. Users are also allowed to embed
pointers to some kernel types (with __kptr and __kptr_ref BTF tags). Will the
kernel preserve backwards compatibility for these features?

A: It depends. For bpf_spin_lock, bpf_timer: YES, for kptr and everything else:
NO, but see below.

For struct types that have been added already, like bpf_spin_lock and bpf_timer,
the kernel will preserve backwards compatibility, as they are part of UAPI.

For kptrs, they are also part of UAPI, but only with respect to the kptr
mechanism. The types that you can use with a __kptr and __kptr_ref tagged
pointer in your struct are NOT part of the UAPI contract. The supported types can
and will change across kernel releases. However, operations like accessing kptr
fields and bpf_kptr_xchg() helper will continue to be supported across kernel
releases for the supported types.

For any other supported struct type, unless explicitly stated in this document
and added to bpf.h UAPI header, such types can and will arbitrarily change their
size, type, and alignment, or any other user visible API or ABI detail across
kernel releases. The users must adapt their BPF programs to the new changes and
update them to make sure their programs continue to work correctly.

NOTE: BPF subsystem specially reserves the 'bpf\_' prefix for type names, in
order to introduce more special fields in the future. Hence, user programs must
avoid defining types with 'bpf\_' prefix to not be broken in future releases.
In other words, no backwards compatibility is guaranteed if one using a type
in BTF with 'bpf\_' prefix.

Q: What is the compatibility story for special BPF types in allocated objects?
------------------------------------------------------------------------------
Q: Same as above, but for allocated objects (i.e. objects allocated using
bpf_obj_new for user defined types). Will the kernel preserve backwards
compatibility for these features?

A: NO.

Unlike map value types, there are no stability guarantees for this case. The
whole API to work with allocated objects and any support for special fields
inside them is unstable (since it is exposed through kfuncs).