blob: e65953d1dfc64ce0588bbadd2feabbf03f1855a8 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <assert.h>
#include <limits.h>
#include <zircon/device/ioctl-wrapper.h>
#include <zircon/device/ioctl.h>
#include <zircon/types.h>
// Get information about the underlying block device.
#define IOCTL_BLOCK_GET_INFO \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 1)
// Get the type GUID of the partition (if one exists)
#define IOCTL_BLOCK_GET_TYPE_GUID \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 2)
// Get the GUID of the partition (if one exists)
#define IOCTL_BLOCK_GET_PARTITION_GUID \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 3)
// Get the name of the partition (if one exists)
#define IOCTL_BLOCK_GET_NAME \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 4)
// Rebind the block device (if supported)
#define IOCTL_BLOCK_RR_PART \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 5)
// Set up a FIFO-based server on the block device; acquire the handle to it
#define IOCTL_BLOCK_GET_FIFOS \
IOCTL(IOCTL_KIND_GET_HANDLE, IOCTL_FAMILY_BLOCK, 6)
// Attach a VMO to the currently running FIFO server
#define IOCTL_BLOCK_ATTACH_VMO \
IOCTL(IOCTL_KIND_SET_HANDLE, IOCTL_FAMILY_BLOCK, 7)
// Allocate a txn with the currently running FIFO server
#define IOCTL_BLOCK_ALLOC_TXN \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 8)
// Free a txn from the currently running FIFO server
#define IOCTL_BLOCK_FREE_TXN \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 9)
// Shut down the fifo server, waiting for it to be ready to be started again.
// Only necessary to guarantee availibility to the next fifo server client;
// otherwise, closing the client fifo is sufficient to shut down the server.
#define IOCTL_BLOCK_FIFO_CLOSE \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 10)
// Allocate a virtual partition with the requested length.
#define IOCTL_BLOCK_FVM_ALLOC_PARTITION \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 11)
// Extend a virtual partition.
#define IOCTL_BLOCK_FVM_EXTEND \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 12)
// Shink a virtual partition. Returns "success" if ANY slices are
// freed, even if part of the requested range contains unallocated slices.
#define IOCTL_BLOCK_FVM_SHRINK \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 13)
// Given a handle to a partition, destroy it.
#define IOCTL_BLOCK_FVM_DESTROY_PARTITION \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 14)
// Returns the total number of vslices and slice size for an FVM partition
#define IOCTL_BLOCK_FVM_QUERY \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 15)
// Given a number of initial vslices, returns the number of contiguous allocated
// (or unallocated) vslices starting from each vslice.
#define IOCTL_BLOCK_FVM_VSLICE_QUERY \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 16)
// Atomically marks a vpartition (by instance GUID) as inactive, while finding
// another partition (by instance GUID) and marking it as active.
//
// If the "old" partition does not exist, the GUID is ignored.
// If the "old" partition is the same as the "new" partition, the "old"
// GUID is ignored (as in, "Upgrade" only activates).
// If the "new" partition does not exist, |ZX_ERR_NOT_FOUND| is returned.
//
// This function does not destroy the "old" partition, it just marks it as
// inactive -- to reclaim that space, the "old" partition must be explicitly
// destroyed. This destruction can also occur automatically when the FVM driver
// is rebound (i.e., on reboot).
//
// This function may be useful for A/B updates within the FVM,
// since it will allow "activating" updated partitions.
#define IOCTL_BLOCK_FVM_UPGRADE \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 17)
// Prints stats about the block device to the provided buffer and optionally
// clears the counters
#define IOCTL_BLOCK_GET_STATS \
IOCTL(IOCTL_KIND_DEFAULT, IOCTL_FAMILY_BLOCK, 18)
// Block Impl ioctls (specific to each block device):
#define BLOCK_FLAG_READONLY 0x00000001
#define BLOCK_FLAG_REMOVABLE 0x00000002
#define BLOCK_FLAG_BOOTPART 0x00000004 // block device has bootdata partition map
// provided by device metadata
typedef struct {
uint64_t block_count; // The number of blocks in this block device
uint32_t block_size; // The size of a single block
uint32_t max_transfer_size; // Max worst-case size in bytes per transfer, 0 is no maximum
uint32_t flags;
uint32_t reserved;
} block_info_t;
typedef struct {
size_t max_concur; // The maximum number of concurrent ops
size_t max_pending; // The maximum number of pending block ops
size_t total_ops; // Total number of block ops processed
size_t total_blocks; // Total number of blocks processed
} block_stats_t;
// ssize_t ioctl_block_get_info(int fd, block_info_t* out);
IOCTL_WRAPPER_OUT(ioctl_block_get_info, IOCTL_BLOCK_GET_INFO, block_info_t);
// ssize_t ioctl_block_get_type_guid(int fd, void* out, size_t out_len);
IOCTL_WRAPPER_VAROUT(ioctl_block_get_type_guid, IOCTL_BLOCK_GET_TYPE_GUID, void);
// ssize_t ioctl_block_get_partition_guid(int fd, void* out, size_t out_len);
IOCTL_WRAPPER_VAROUT(ioctl_block_get_partition_guid, IOCTL_BLOCK_GET_PARTITION_GUID, void);
// ssize_t ioctl_block_get_name(int fd, char* out, size_t out_len);
IOCTL_WRAPPER_VAROUT(ioctl_block_get_name, IOCTL_BLOCK_GET_NAME, char);
// ssize_t ioctl_block_rr_part(int fd);
IOCTL_WRAPPER(ioctl_block_rr_part, IOCTL_BLOCK_RR_PART);
// TODO(smklein): Move these to a separate file
// Block Device ioctls (shared between all block devices):
// ssize_t ioctl_block_get_fifos(int fd, zx_handle_t* fifo_out);
IOCTL_WRAPPER_OUT(ioctl_block_get_fifos, IOCTL_BLOCK_GET_FIFOS, zx_handle_t);
typedef uint16_t vmoid_t;
// Dummy vmoid value reserved for "invalid". Will never be allocated; can be
// used as a local value for unallocated / freed ID.
#define VMOID_INVALID 0
// ssize_t ioctl_block_attach_vmo(int fd, zx_handle_t* in, vmoid_t* out_vmoid);
IOCTL_WRAPPER_INOUT(ioctl_block_attach_vmo, IOCTL_BLOCK_ATTACH_VMO, zx_handle_t, vmoid_t);
#define MAX_TXN_COUNT 256
typedef uint16_t txnid_t;
// Dummy TXNID value reserved for "invalid". Will never be allocated; can be
// used as a local value for unallocated / freed ID.
#define TXNID_INVALID 0xFFFF
static_assert(TXNID_INVALID > MAX_TXN_COUNT, "Invalid Txn ID may be valid");
// ssize_t ioctl_block_alloc_txn(int fd, txnid_t* out_txnid);
IOCTL_WRAPPER_OUT(ioctl_block_alloc_txn, IOCTL_BLOCK_ALLOC_TXN, txnid_t);
// ssize_t ioctl_block_free_txn(int fd, const size_t* in_txnid);
IOCTL_WRAPPER_IN(ioctl_block_free_txn, IOCTL_BLOCK_FREE_TXN, txnid_t);
// ssize_t ioctl_block_fifo_close(int fd);
IOCTL_WRAPPER(ioctl_block_fifo_close, IOCTL_BLOCK_FIFO_CLOSE);
#define GUID_LEN 16
#define NAME_LEN 24
#define MAX_FVM_VSLICE_REQUESTS 16
typedef struct {
size_t slice_count;
uint8_t type[GUID_LEN];
uint8_t guid[GUID_LEN];
char name[NAME_LEN];
uint32_t flags; // Refer to fvm.h for options here; default is zero.
} alloc_req_t;
// ssize_t ioctl_block_fvm_alloc_partition(int fd, const alloc_req_t* req);
IOCTL_WRAPPER_IN(ioctl_block_fvm_alloc_partition, IOCTL_BLOCK_FVM_ALLOC_PARTITION, alloc_req_t);
typedef struct {
size_t offset; // Both in units of "slice". "0" = slice 0, "1" = slice 1, etc...
size_t length;
} extend_request_t;
// ssize_t ioctl_block_fvm_extend(int fd, const extend_request_t* request);
IOCTL_WRAPPER_IN(ioctl_block_fvm_extend, IOCTL_BLOCK_FVM_EXTEND, extend_request_t);
// ssize_t ioctl_block_fvm_shrink(int fd, const extend_request_t* request);
IOCTL_WRAPPER_IN(ioctl_block_fvm_shrink, IOCTL_BLOCK_FVM_SHRINK, extend_request_t);
// ssize_t ioctl_block_fvm_destroy_partition(int fd);
IOCTL_WRAPPER(ioctl_block_fvm_destroy_partition, IOCTL_BLOCK_FVM_DESTROY_PARTITION);
typedef struct {
bool allocated; // true if vslices are allocated, false otherwise
size_t count; // number of contiguous vslices
} vslice_range_t;
typedef struct {
size_t count; // number of elements in vslice_start
size_t vslice_start[MAX_FVM_VSLICE_REQUESTS]; // vslices to query from
} query_request_t;
typedef struct {
size_t count; // number of elements in vslice_range
vslice_range_t vslice_range[MAX_FVM_VSLICE_REQUESTS]; // number of contiguous vslices
// that are allocated (or unallocated)
} query_response_t;
typedef struct {
size_t slice_size; // Size of a single slice, in bytes
size_t vslice_count; // Number of addressable slices
} fvm_info_t;
// ssize_t ioctl_block_fvm_query(int fd, fvm_info_t* info);
IOCTL_WRAPPER_OUT(ioctl_block_fvm_query, IOCTL_BLOCK_FVM_QUERY, fvm_info_t);
// ssize_t ioctl_block_fvm_vslice_query(int fd, query_request_t* request,
// query_response_t* response);
IOCTL_WRAPPER_INOUT(ioctl_block_fvm_vslice_query, IOCTL_BLOCK_FVM_VSLICE_QUERY,
query_request_t, query_response_t);
typedef struct {
uint8_t old_guid[GUID_LEN];
uint8_t new_guid[GUID_LEN];
} upgrade_req_t;
// ssize_t ioctl_block_fvm_upgrade(int fd, const upgrade_req_t* req);
IOCTL_WRAPPER_IN(ioctl_block_fvm_upgrade, IOCTL_BLOCK_FVM_UPGRADE, upgrade_req_t);
// ssize_t ioctl_block_get_stats(int fd, bool clear, block_stats_t* out)
IOCTL_WRAPPER_INOUT(ioctl_block_get_stats, IOCTL_BLOCK_GET_STATS, bool, block_stats_t);
// Multiple Block IO operations may be sent at once before a response is actually sent back.
// Block IO ops may be sent concurrently to different vmoids, and they also may be sent
// to different transactions at any point in time. Up to MAX_TXN_COUNT transactions may
// be allocated at any point in time.
//
// "Transactions" are allocated with the "alloc_txn" ioctl. Allocating a transaction allows
// multiple message to be buffered at once on a single txn before receiving a response.
// Once a txn has been allocated, it can be re-used many times. It is recommended that
// transactions are allocated on a "per-thread" basis, and only freed on thread teardown.
//
// The protocol to communicate with a single txn is as follows:
// 1) SEND [N - 1] messages with an allocated txnid for any value of 1 <= N.
// The BLOCKIO_TXN_END flag is not set for this step.
// 2) SEND a final Nth message with the same txnid, but also the BLOCKIO_TXN_END flag.
// 3) RECEIVE a single response from the Block IO server after all N requests have completed.
// This response is sent once all operations either complete or a single operation fails.
// At this point, step (1) may begin again without reallocating the txn.
//
// For BLOCKIO_READ and BLOCKIO_WRITE, N may be greater than 1.
// Otherwise, N == 1 (skipping step (1) in the protocol above).
//
// Notes:
// - txnids may operate on any number of vmoids at once.
// - If additional requests are sent on the same txnid before step (3) has completed, then
// the additional request will not be processed. If BLOCKIO_TXN_END is set, an error will
// be returned. Otherwise, the request will be silently dropped.
// - The only requests that receive responses are ones which have the BLOCKIO_TXN_END flag
// set. This is the case for both successful and erroneous requests. This property allows
// the Block IO server to send back a response on the FIFO without waiting.
//
// For example, the following is a valid sequence of transactions:
// -> (txnid = 1, vmoid = 1, OP = Write)
// -> (txnid = 1, vmoid = 2, OP = Write)
// -> (txnid = 2, vmoid = 3, OP = Write | Want Reply)
// <- Response sent to txnid = 2
// -> (txnid = 1, vmoid = 1, OP = Read | Want Reply)
// <- Response sent to txnid = 1
// -> (txnid = 3, vmoid = 1, OP = Write)
// -> (txnid = 3, vmoid = 1, OP = Read | Want Reply)
// <- Repsonse sent to txnid = 3
//
// Each transaction reads or writes up to 'length' blocks from the device, starting at 'dev_offset'
// blocks, into the VMO associated with 'vmoid', starting at 'vmo_offset' blocks. If the
// transaction is out of range, for example if 'length' is too large or if 'dev_offset' is beyond
// the end of the device, ZX_ERR_OUT_OF_RANGE is returned.
// Reads from the Block device into the VMO
#define BLOCKIO_READ 0x00000001
// Writes to the Block device from the VMO
#define BLOCKIO_WRITE 0x00000002
// Write any cached data to nonvolatile storage.
// Implies BARRIER_BEFORE and BARRIER_AFTER.
#define BLOCKIO_FLUSH 0x00000003
// Detaches the VMO from the block device.
#define BLOCKIO_CLOSE_VMO 0x00000004
#define BLOCKIO_OP_MASK 0x000000FF
// Require that this operation will not begin until all prior operations
// have completed.
#define BLOCKIO_BARRIER_BEFORE 0x00000100
// Require that this operation must complete before additional operations begin.
#define BLOCKIO_BARRIER_AFTER 0x00000200
// Respond after request (and all previous) have completed
#define BLOCKIO_TXN_END 0x00000400
#define BLOCKIO_FLAG_MASK 0x0000FF00
typedef struct {
txnid_t txnid;
vmoid_t vmoid;
uint32_t opcode;
uint64_t length;
uint64_t vmo_offset;
uint64_t dev_offset;
} block_fifo_request_t;
typedef struct {
txnid_t txnid;
uint16_t reserved0;
zx_status_t status;
uint32_t count; // The number of messages in the transaction completed by the block server.
uint32_t reserved1;
uint64_t reserved2;
uint64_t reserved3;
} block_fifo_response_t;
static_assert(sizeof(block_fifo_request_t) == sizeof(block_fifo_response_t),
"FIFO messages are the same size in both directions");
#define BLOCK_FIFO_ESIZE (sizeof(block_fifo_request_t))
#define BLOCK_FIFO_MAX_DEPTH (4096 / BLOCK_FIFO_ESIZE)