Documentation
¶
Overview ¶
Package ublk provides the main API for creating userspace block devices
Index ¶
- Constants
- Variables
- func IsCode(err error, code UblkErrorCode) bool
- func IsErrno(err error, errno syscall.Errno) bool
- type Backend
- type Device
- func (d *Device) BlockPath() string
- func (d *Device) BlockSize() int
- func (d *Device) CharDevicePath() string
- func (d *Device) Close() error
- func (d *Device) DeviceID() uint32
- func (d *Device) Info() DeviceInfo
- func (d *Device) IsRunning() bool
- func (d *Device) Metrics() *Metrics
- func (d *Device) MetricsSnapshot() MetricsSnapshot
- func (d *Device) NumQueues() int
- func (d *Device) QueueDepth() int
- func (d *Device) Size() int64
- func (d *Device) Start(ctx context.Context) error
- func (d *Device) State() DeviceState
- func (d *Device) Stop() error
- type DeviceInfo
- type DeviceParams
- type DeviceState
- type DiscardBackend
- type Error
- type Logger
- type Metrics
- func (m *Metrics) RecordDiscard(bytes uint64, latencyNs uint64, success bool)
- func (m *Metrics) RecordFlush(latencyNs uint64, success bool)
- func (m *Metrics) RecordQueueDepth(depth uint32)
- func (m *Metrics) RecordRead(bytes uint64, latencyNs uint64, success bool)
- func (m *Metrics) RecordWrite(bytes uint64, latencyNs uint64, success bool)
- func (m *Metrics) Reset()
- func (m *Metrics) Snapshot() MetricsSnapshot
- func (m *Metrics) Stop()
- type MetricsObserver
- func (o *MetricsObserver) ObserveDiscard(bytes uint64, latencyNs uint64, success bool)
- func (o *MetricsObserver) ObserveFlush(latencyNs uint64, success bool)
- func (o *MetricsObserver) ObserveQueueDepth(depth uint32)
- func (o *MetricsObserver) ObserveRead(bytes uint64, latencyNs uint64, success bool)
- func (o *MetricsObserver) ObserveWrite(bytes uint64, latencyNs uint64, success bool)
- type MetricsSnapshot
- type MockBackend
- func (m *MockBackend) CallCounts() map[string]int
- func (m *MockBackend) Close() error
- func (m *MockBackend) Discard(offset, length int64) error
- func (m *MockBackend) Flush() error
- func (m *MockBackend) IsClosed() bool
- func (m *MockBackend) IsFlushed() bool
- func (m *MockBackend) IsSynced() bool
- func (m *MockBackend) ReadAt(p []byte, off int64) (int, error)
- func (m *MockBackend) Reset()
- func (m *MockBackend) Resize(newSize int64) error
- func (m *MockBackend) SetCustomStats(stats map[string]interface{})
- func (m *MockBackend) Size() int64
- func (m *MockBackend) Stats() map[string]interface{}
- func (m *MockBackend) Sync() error
- func (m *MockBackend) SyncRange(offset, length int64) error
- func (m *MockBackend) WriteAt(p []byte, off int64) (int, error)
- func (m *MockBackend) WriteZeroes(offset, length int64) error
- type NoOpObserver
- type Observer
- type Options
- type ResizeBackend
- type StatBackend
- type SyncBackend
- type UblkErrorCode
- type WriteZeroesBackend
Constants ¶
const ( DefaultQueueDepth = constants.DefaultQueueDepth DefaultLogicalBlockSize = constants.DefaultLogicalBlockSize DefaultMaxIOSize = constants.DefaultMaxIOSize DefaultDiscardAlignment = constants.DefaultDiscardAlignment DefaultDiscardGranularity = constants.DefaultDiscardGranularity DefaultMaxDiscardSectors = constants.DefaultMaxDiscardSectors DefaultMaxDiscardSegments = constants.DefaultMaxDiscardSegments AutoAssignDeviceID = constants.AutoAssignDeviceID IOBufferSizePerTag = constants.IOBufferSizePerTag )
Re-export constants for public API
const (
// NoQueue indicates that an error is not associated with a specific queue
NoQueue = -1
)
Variables ¶
var ( ErrNotImplemented = &Error{Code: ErrCodeNotImplemented, Msg: "not implemented", Queue: NoQueue} ErrDeviceNotFound = &Error{Code: ErrCodeDeviceNotFound, Msg: "device not found", Queue: NoQueue} ErrDeviceBusy = &Error{Code: ErrCodeDeviceBusy, Msg: "device busy", Queue: NoQueue} ErrInvalidParameters = &Error{Code: ErrCodeInvalidParameters, Msg: "invalid parameters", Queue: NoQueue} ErrKernelNotSupported = &Error{Code: ErrCodeKernelNotSupported, Msg: "kernel does not support ublk", Queue: NoQueue} ErrPermissionDenied = &Error{Code: ErrCodePermissionDenied, Msg: "permission denied", Queue: NoQueue} ErrInsufficientMemory = &Error{Code: ErrCodeInsufficientMemory, Msg: "insufficient memory", Queue: NoQueue} ErrIOError = &Error{Code: ErrCodeIOError, Msg: "I/O error", Queue: NoQueue} ErrTimeout = &Error{Code: ErrCodeTimeout, Msg: "timeout", Queue: NoQueue} ErrDeviceOffline = &Error{Code: ErrCodeDeviceOffline, Msg: "device offline", Queue: NoQueue} )
Sentinel errors for use with errors.Is()
var LatencyBuckets = []uint64{
1_000,
10_000,
100_000,
1_000_000,
10_000_000,
100_000_000,
1_000_000_000,
10_000_000_000,
}
LatencyBuckets defines the latency histogram buckets in nanoseconds. Buckets cover from 1us to 10s with logarithmic spacing.
Functions ¶
func IsCode ¶
func IsCode(err error, code UblkErrorCode) bool
IsCode checks if an error matches a specific error code
Types ¶
type Backend ¶
type Backend interface {
// ReadAt reads len(p) bytes into p starting at offset off.
// It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
// When ReadAt returns n < len(p), it returns a non-nil error explaining
// why more bytes were not returned.
//
// If the n = len(p) bytes returned by ReadAt are at the end of the input source,
// ReadAt may return either err == nil or err == io.EOF.
//
// Implementations must not retain p.
ReadAt(p []byte, off int64) (n int, err error)
// WriteAt writes len(p) bytes from p to the underlying data stream at offset off.
// It returns the number of bytes written from p (0 <= n <= len(p)) and
// any error encountered that caused the write to stop early.
// WriteAt must return a non-nil error if it returns n < len(p).
//
// If WriteAt is writing to a destination with a seek offset,
// WriteAt should not affect nor be affected by the underlying seek offset.
//
// Implementations must not retain p.
WriteAt(p []byte, off int64) (n int, err error)
// Size returns the size of the backend in bytes.
// This determines the size of the block device as seen by the kernel.
Size() int64
// Close closes the backend and releases any resources.
// After Close is called, no other methods should be called.
Close() error
// Flush flushes any cached writes to stable storage.
// This is called when the block layer issues a flush/fsync request.
Flush() error
}
Backend defines the interface that all ublk backends must implement. This interface is intentionally similar to standard Go interfaces like io.ReaderAt and io.WriterAt for familiarity and composability.
type Device ¶
type Device struct {
// ID is the device ID assigned by the kernel
ID uint32
// Path is the path to the block device (e.g., "/dev/ublkb0")
Path string
// CharPath is the path to the character device (e.g., "/dev/ublkc0")
CharPath string
// Backend is the backend implementation
Backend Backend
// contains filtered or unexported fields
}
Device represents a ublk block device
func Create ¶
func Create(params DeviceParams, options *Options) (*Device, error)
Create creates a ublk device without starting I/O processing. Use this when you need more control over the device lifecycle. After Create, call Start() to begin serving I/O, Stop() to pause, and Close() for full cleanup.
Example:
device, err := ublk.Create(params, options)
if err != nil {
return err
}
defer device.Close()
if err := device.Start(ctx); err != nil {
return err
}
// Device is now serving I/O
func CreateAndServe ¶
CreateAndServe creates a ublk device with the given parameters and starts serving I/O. This is the main entry point for creating ublk devices.
The device will continue serving I/O until: - The context is cancelled - StopAndDelete is called - An unrecoverable error occurs
Example:
backend := mem.New(64 << 20) // 64MB RAM disk params := ublk.DefaultParams(backend) device, err := ublk.CreateAndServe(context.Background(), params, nil)
func (*Device) CharDevicePath ¶
CharDevicePath returns the path to the character device (e.g., "/dev/ublkc0")
func (*Device) Close ¶
Close performs full cleanup: stops I/O (if running) and removes the device. After Close(), the device cannot be reused.
func (*Device) Info ¶
func (d *Device) Info() DeviceInfo
Info returns comprehensive information about the device
func (*Device) MetricsSnapshot ¶
func (d *Device) MetricsSnapshot() MetricsSnapshot
MetricsSnapshot returns a point-in-time snapshot of device metrics
func (*Device) QueueDepth ¶
QueueDepth returns the queue depth configured for this device
func (*Device) Start ¶
Start begins serving I/O requests for a device created with Create(). The context controls the lifetime of I/O processing. Returns an error if the device is already started or has been closed.
func (*Device) State ¶
func (d *Device) State() DeviceState
State returns the current state of the device
type DeviceInfo ¶
type DeviceInfo struct {
ID uint32 `json:"id"`
BlockPath string `json:"block_path"`
CharPath string `json:"char_path"`
State DeviceState `json:"state"`
NumQueues int `json:"num_queues"`
QueueDepth int `json:"queue_depth"`
BlockSize int `json:"block_size"`
Size int64 `json:"size"`
Running bool `json:"running"`
}
DeviceInfo contains comprehensive information about a ublk device
type DeviceParams ¶
type DeviceParams struct {
// Backend provides the storage implementation
Backend Backend
// Device configuration
QueueDepth int // Queue depth per queue (default: 128)
NumQueues int // Number of queues (default: number of CPUs)
LogicalBlockSize int // Logical block size in bytes (default: 512)
MaxIOSize int // Maximum I/O size in bytes (default: 1MB)
// Feature flags
EnableZeroCopy bool // Enable zero-copy if supported
EnableUnprivileged bool // Allow unprivileged operation
EnableUserCopy bool // Use user-copy mode
EnableZoned bool // Enable zoned storage support
EnableIoctlEncode bool // Use ioctl encoding instead of URING_CMD
// Device attributes
ReadOnly bool // Make device read-only
Rotational bool // Device is rotational (HDD-like)
VolatileCache bool // Device has volatile cache
EnableFUA bool // Enable Force Unit Access
// Discard parameters (only used if backend implements DiscardBackend)
DiscardAlignment uint32 // Discard alignment
DiscardGranularity uint32 // Discard granularity
MaxDiscardSectors uint32 // Max sectors per discard
MaxDiscardSegments uint16 // Max segments per discard
// Advanced options
DeviceID int32 // Specific device ID to request (-1 for auto)
DeviceName string // Optional device name
CPUAffinity []int // CPU affinity mask for queue threads
}
DeviceParams contains parameters for creating a ublk device
func DefaultParams ¶
func DefaultParams(backend Backend) DeviceParams
DefaultParams returns default device parameters
type DeviceState ¶
type DeviceState string
DeviceState represents the current state of a ublk device
const ( // DeviceStateCreated indicates the device has been created but not started DeviceStateCreated DeviceState = "created" // DeviceStateRunning indicates the device is actively serving I/O DeviceStateRunning DeviceState = "running" // DeviceStateStopped indicates the device has been stopped but is still registered DeviceStateStopped DeviceState = "stopped" // DeviceStateClosed indicates the device has been fully closed and removed DeviceStateClosed DeviceState = "closed" )
type DiscardBackend ¶
type DiscardBackend interface {
Backend
// Discard discards the data in the given range, making it available for reuse.
// The backend may choose to actually deallocate the space or simply mark it as unused.
// offset and length are in bytes.
Discard(offset, length int64) error
}
DiscardBackend is an optional interface that backends can implement to support TRIM/DISCARD operations efficiently.
type Error ¶
type Error struct {
Op string // Operation that failed (e.g., "CREATE_DEV", "START_DEV")
DevID uint32 // Device ID (0 if not applicable)
Queue int // Queue number (NoQueue if not applicable)
Code UblkErrorCode // High-level error category
Errno syscall.Errno // Kernel errno (0 if not applicable)
Msg string // Human-readable message
Inner error // Wrapped error
}
Error represents a structured ublk error with context and errno mapping
func NewError ¶
func NewError(op string, code UblkErrorCode, msg string) *Error
NewError creates a new structured error
type Logger ¶
type Logger interface {
Printf(format string, args ...interface{})
Debugf(format string, args ...interface{})
}
Logger interface for optional logging.
type Metrics ¶
type Metrics struct {
// I/O operation counters
ReadOps atomic.Uint64 // Total read operations
WriteOps atomic.Uint64 // Total write operations
DiscardOps atomic.Uint64 // Total discard operations
FlushOps atomic.Uint64 // Total flush operations
// Byte counters
ReadBytes atomic.Uint64 // Total bytes read
WriteBytes atomic.Uint64 // Total bytes written
DiscardBytes atomic.Uint64 // Total bytes discarded
// Error counters
ReadErrors atomic.Uint64 // Read operation errors
WriteErrors atomic.Uint64 // Write operation errors
DiscardErrors atomic.Uint64 // Discard operation errors
FlushErrors atomic.Uint64 // Flush operation errors
// Queue statistics
QueueDepthTotal atomic.Uint64 // Cumulative queue depth samples
QueueDepthCount atomic.Uint64 // Number of queue depth measurements
MaxQueueDepth atomic.Uint32 // Maximum observed queue depth
// Performance tracking
TotalLatencyNs atomic.Uint64 // Cumulative operation latency in nanoseconds
OpCount atomic.Uint64 // Total operations (for average latency calculation)
// Latency histogram buckets (cumulative counts)
// Each bucket[i] contains the count of operations with latency <= LatencyBuckets[i]
LatencyBuckets [numLatencyBuckets]atomic.Uint64
// Device lifecycle
StartTime atomic.Int64 // Device start timestamp (UnixNano)
StopTime atomic.Int64 // Device stop timestamp (UnixNano)
}
Metrics tracks performance and operational statistics for ublk devices
func (*Metrics) RecordDiscard ¶
RecordDiscard records a discard operation
func (*Metrics) RecordFlush ¶
RecordFlush records a flush operation
func (*Metrics) RecordQueueDepth ¶
RecordQueueDepth records current queue depth for statistics
func (*Metrics) RecordRead ¶
RecordRead records a read operation
func (*Metrics) RecordWrite ¶
RecordWrite records a write operation
func (*Metrics) Reset ¶
func (m *Metrics) Reset()
Reset resets all metrics counters (useful for testing)
func (*Metrics) Snapshot ¶
func (m *Metrics) Snapshot() MetricsSnapshot
Snapshot creates a point-in-time snapshot of metrics
type MetricsObserver ¶
type MetricsObserver struct {
// contains filtered or unexported fields
}
MetricsObserver implements Observer using the built-in Metrics
func NewMetricsObserver ¶
func NewMetricsObserver(m *Metrics) *MetricsObserver
NewMetricsObserver creates an observer that records to the given metrics
func (*MetricsObserver) ObserveDiscard ¶
func (o *MetricsObserver) ObserveDiscard(bytes uint64, latencyNs uint64, success bool)
func (*MetricsObserver) ObserveFlush ¶
func (o *MetricsObserver) ObserveFlush(latencyNs uint64, success bool)
func (*MetricsObserver) ObserveQueueDepth ¶
func (o *MetricsObserver) ObserveQueueDepth(depth uint32)
func (*MetricsObserver) ObserveRead ¶
func (o *MetricsObserver) ObserveRead(bytes uint64, latencyNs uint64, success bool)
func (*MetricsObserver) ObserveWrite ¶
func (o *MetricsObserver) ObserveWrite(bytes uint64, latencyNs uint64, success bool)
type MetricsSnapshot ¶
type MetricsSnapshot struct {
// I/O operations
ReadOps uint64
WriteOps uint64
DiscardOps uint64
FlushOps uint64
// Bytes transferred
ReadBytes uint64
WriteBytes uint64
DiscardBytes uint64
// Error counts
ReadErrors uint64
WriteErrors uint64
DiscardErrors uint64
FlushErrors uint64
// Queue statistics
AvgQueueDepth float64
MaxQueueDepth uint32
// Performance
AvgLatencyNs uint64
UptimeNs uint64
// Latency percentiles (in nanoseconds)
LatencyP50Ns uint64 // 50th percentile (median)
LatencyP99Ns uint64 // 99th percentile
LatencyP999Ns uint64 // 99.9th percentile
// Histogram bucket counts (cumulative)
LatencyHistogram [numLatencyBuckets]uint64
// Computed statistics
ReadIOPS float64 // Operations per second
WriteIOPS float64
ReadBandwidth float64 // Bytes per second
WriteBandwidth float64
TotalOps uint64
TotalBytes uint64
ErrorRate float64 // Percentage of failed operations
}
Snapshot returns a point-in-time snapshot of metrics
type MockBackend ¶
type MockBackend struct {
// contains filtered or unexported fields
}
MockBackend provides a mock implementation of Backend for testing. It implements all optional interfaces and tracks method calls for verification.
func NewMockBackend ¶
func NewMockBackend(size int64) *MockBackend
NewMockBackend creates a new mock backend with the specified size. This is useful for unit testing applications that use ublk backends.
func (*MockBackend) CallCounts ¶
func (m *MockBackend) CallCounts() map[string]int
CallCounts returns the number of times each method has been called
func (*MockBackend) Close ¶
func (m *MockBackend) Close() error
Close implements the Backend interface
func (*MockBackend) Discard ¶
func (m *MockBackend) Discard(offset, length int64) error
Discard implements the DiscardBackend interface
func (*MockBackend) Flush ¶
func (m *MockBackend) Flush() error
Flush implements the Backend interface
func (*MockBackend) IsClosed ¶
func (m *MockBackend) IsClosed() bool
IsClosed returns true if the backend has been closed
func (*MockBackend) IsFlushed ¶
func (m *MockBackend) IsFlushed() bool
IsFlushed returns true if Flush has been called
func (*MockBackend) IsSynced ¶
func (m *MockBackend) IsSynced() bool
IsSynced returns true if Sync or SyncRange has been called
func (*MockBackend) ReadAt ¶
func (m *MockBackend) ReadAt(p []byte, off int64) (int, error)
ReadAt implements the Backend interface
func (*MockBackend) Reset ¶
func (m *MockBackend) Reset()
Reset resets all call counters and state flags
func (*MockBackend) Resize ¶
func (m *MockBackend) Resize(newSize int64) error
Resize implements the ResizeBackend interface
func (*MockBackend) SetCustomStats ¶
func (m *MockBackend) SetCustomStats(stats map[string]interface{})
SetCustomStats allows setting custom statistics for testing
func (*MockBackend) Stats ¶
func (m *MockBackend) Stats() map[string]interface{}
Stats implements the StatBackend interface
func (*MockBackend) Sync ¶
func (m *MockBackend) Sync() error
Sync implements the SyncBackend interface
func (*MockBackend) SyncRange ¶
func (m *MockBackend) SyncRange(offset, length int64) error
SyncRange implements the SyncBackend interface
func (*MockBackend) WriteAt ¶
func (m *MockBackend) WriteAt(p []byte, off int64) (int, error)
WriteAt implements the Backend interface
func (*MockBackend) WriteZeroes ¶
func (m *MockBackend) WriteZeroes(offset, length int64) error
WriteZeroes implements the WriteZeroesBackend interface
type NoOpObserver ¶
type NoOpObserver struct{}
NoOpObserver is a no-op implementation of Observer
func (NoOpObserver) ObserveDiscard ¶
func (NoOpObserver) ObserveDiscard(uint64, uint64, bool)
func (NoOpObserver) ObserveFlush ¶
func (NoOpObserver) ObserveFlush(uint64, bool)
func (NoOpObserver) ObserveQueueDepth ¶
func (NoOpObserver) ObserveQueueDepth(uint32)
func (NoOpObserver) ObserveRead ¶
func (NoOpObserver) ObserveRead(uint64, uint64, bool)
func (NoOpObserver) ObserveWrite ¶
func (NoOpObserver) ObserveWrite(uint64, uint64, bool)
type Observer ¶
type Observer interface {
// ObserveRead is called for each read operation
ObserveRead(bytes uint64, latencyNs uint64, success bool)
// ObserveWrite is called for each write operation
ObserveWrite(bytes uint64, latencyNs uint64, success bool)
// ObserveDiscard is called for each discard operation
ObserveDiscard(bytes uint64, latencyNs uint64, success bool)
// ObserveFlush is called for each flush operation
ObserveFlush(latencyNs uint64, success bool)
// ObserveQueueDepth is called periodically with current queue depth
ObserveQueueDepth(depth uint32)
}
Observer interface allows pluggable metrics collection
type Options ¶
type Options struct {
// Context for cancellation (if nil, uses context.Background())
Context context.Context
// Logger for debug/info messages (if nil, no logging)
Logger Logger
// Observer for metrics collection (if nil, uses no-op observer)
Observer Observer
}
Options contains additional options for device creation
type ResizeBackend ¶
type ResizeBackend interface {
Backend
// Resize changes the size of the backend.
// The new size must be greater than or equal to 0.
// If the new size is smaller, data may be truncated.
// If the new size is larger, the new space should read as zeros.
Resize(newSize int64) error
}
ResizeBackend is an optional interface for backends that support resizing.
type StatBackend ¶
type StatBackend interface {
Backend
// Stats returns backend-specific statistics.
// The returned map contains string keys with numeric values.
Stats() map[string]interface{}
}
StatBackend is an optional interface that provides device statistics.
type SyncBackend ¶
type SyncBackend interface {
Backend
// Sync synchronizes the backend state to stable storage.
// This is different from Flush in that it may also sync metadata.
Sync() error
// SyncRange synchronizes only the specified range to stable storage.
// This can be more efficient than syncing the entire backend.
SyncRange(offset, length int64) error
}
SyncBackend is an optional interface for fine-grained sync control.
type UblkErrorCode ¶
type UblkErrorCode string
UblkErrorCode represents high-level error categories
const ( ErrCodeNotImplemented UblkErrorCode = "not implemented" ErrCodeDeviceNotFound UblkErrorCode = "device not found" ErrCodeDeviceBusy UblkErrorCode = "device busy" ErrCodeInvalidParameters UblkErrorCode = "invalid parameters" ErrCodeKernelNotSupported UblkErrorCode = "kernel does not support ublk" ErrCodePermissionDenied UblkErrorCode = "permission denied" ErrCodeInsufficientMemory UblkErrorCode = "insufficient memory" ErrCodeIOError UblkErrorCode = "I/O error" ErrCodeTimeout UblkErrorCode = "timeout" ErrCodeDeviceOffline UblkErrorCode = "device offline" )
type WriteZeroesBackend ¶
type WriteZeroesBackend interface {
Backend
// WriteZeroes efficiently writes zeros to the given range.
// This is more efficient than WriteAt with a zero-filled buffer.
// offset and length are in bytes.
WriteZeroes(offset, length int64) error
}
WriteZeroesBackend is an optional interface for efficient zero-writing.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
ublk-mem
command
|
|
|
internal
|
|
|
interfaces
Package interfaces provides internal interface definitions for go-ublk.
|
Package interfaces provides internal interface definitions for go-ublk. |
|
logging
Package logging provides simple logging for the go-ublk project
|
Package logging provides simple logging for the go-ublk project |
|
uapi
Package uapi provides Linux kernel UAPI definitions for ublk
|
Package uapi provides Linux kernel UAPI definitions for ublk |
|
uring
Package uring provides interfaces for io_uring operations
|
Package uring provides interfaces for io_uring operations |