Documentation
¶
Overview ¶
Package balloon is a pure-Go virtio-balloon (memory balloon) driver. It drives a modern (Virtio 1.0+) PCI virtio-balloon device through the transport interfaces defined in github.com/go-virtio/common; the same code drives a UEFI-backed device, a bare-metal device, or a virtio-mmio device depending on which common.Transport implementation the caller supplies.
Scope — like go-virtio/blk this package owns device bring-up, the two page-transfer virtqueues (inflateq + deflateq), and the on-the-wire page-frame-number (PFN) array format (Virtio 1.1 §5.5.6), exposing an Inflate / Deflate API. "Inflating" the balloon hands guest pages back to the host (shrinking the guest's effective RAM); "deflating" reclaims them. The device-config `num_pages` field is the host's desired balloon size in 4096-byte pages.
- Modern transport (VIRTIO_F_VERSION_1 mandatory). Legacy devices are rejected by the common init sequence.
- Only VIRTIO_F_VERSION_1 is negotiated — in particular VIRTIO_BALLOON_F_STATS_VQ is NOT negotiated, so the device exposes exactly two queues (inflateq, deflateq) and there is no stats queue.
- A request posts an array of le32 PFNs in a single DMA buffer to inflateq (or deflateq), device-readable (the host reads the PFN list), then rings the doorbell and busy-polls the used ring. Each buffer carries at most MaxPFNsPerBuffer (256) PFNs; larger requests are split into multiple buffers.
Limitation — the driver does NOT write the `actual` field back to device config. common.ModernConfig exposes only DeviceCfgRead* (no device-config write path), so the spec's "tell the device the current balloon size" step (Virtio 1.1 §5.5.6.1) cannot be performed through this transport. The current balloon size is tracked driver-side in the Actual field instead. This is an intentional, noted limitation.
References:
- Virtio 1.1 §5.5 "Memory Balloon Device" — device-type 5 binding.
- Virtio 1.1 §5.5.5 "Device configuration layout" — le32 num_pages, le32 actual.
- Virtio 1.1 §5.5.6 "Device Operation" — inflate/deflate PFN arrays.
- Virtio 1.1 §3.1.1 "Device Initialization".
Index ¶
Constants ¶
const ( InflateQueueIdx uint16 = 0 DeflateQueueIdx uint16 = 1 )
InflateQueueIdx and DeflateQueueIdx are the two virtqueue indices the driver uses (Virtio 1.1 §5.5.2): inflateq = 0, deflateq = 1.
const AcceptedFeatures uint64 = common.FeatureVersion1
AcceptedFeatures is the feature mask the driver negotiates ON — only the non-negotiable VIRTIO_F_VERSION_1. (VIRTIO_BALLOON_F_STATS_VQ is deliberately NOT accepted, so no stats queue exists.)
const DeviceType uint16 = 5
DeviceType is the virtio device-type encoding for virtio-balloon (Virtio 1.1 §5.5.1).
const MaxPFNsPerBuffer = 256
MaxPFNsPerBuffer is the maximum number of le32 PFNs the driver packs into a single DMA buffer before posting it. Requests larger than this are split into multiple buffers. 256 PFNs = 1024 bytes, well within a single 4 KiB page.
const PFNShift = 12
PFNShift is VIRTIO_BALLOON_PFN_SHIFT (Virtio 1.1 §5.5.6): a page frame number is a physical address shifted right by 12 (the balloon always works in 4096-byte units regardless of the host page size).
const PFNSize = 4
PFNSize is the on-the-wire size of one page frame number — a le32 (Virtio 1.1 §5.5.6).
const QueueSize uint16 = 16
QueueSize is the desired ring size for each page-transfer queue (clamped + rounded during setup). One request occupies a single descriptor, so this bounds the number of in-flight buffers; the driver issues them one at a time.
const TxPollIterations = 200000
TxPollIterations is the default busy-poll budget for one posted buffer.
Variables ¶
var ( ErrNotModernDevice = commonBalloonError("go-virtio/balloon: device doesn't offer VIRTIO_F_VERSION_1 (legacy-only)") ErrFeaturesNotOK = commonBalloonError("go-virtio/balloon: FEATURES_OK status bit didn't stick after DriverFeature write") ErrInitWrongDeviceID = commonBalloonError("go-virtio/balloon: PCI device ID is not 0x1045 (modern balloon device)") ErrQueueNotAvailable = commonBalloonError("go-virtio/balloon: device reports QueueSize=0 for a page-transfer queue") ErrRequestTimeout = commonBalloonError("go-virtio/balloon: request poll timeout (device did not consume the PFN buffer)") ErrZeroPages = commonBalloonError("go-virtio/balloon: page count must be positive") ErrDeflateTooMany = commonBalloonError("go-virtio/balloon: deflate count exceeds the number of held (ballooned) pages") )
Sentinel errors for the virtio-balloon path.
Functions ¶
func AcceptFeatures ¶
AcceptFeatures returns the negotiated mask (requires VERSION_1).
Types ¶
type VirtioBalloon ¶
type VirtioBalloon struct {
// Cfg is the modern-transport handle.
Cfg *common.ModernConfig
// NumPages is the host's desired balloon size in 4096-byte pages,
// read from DeviceCfg `num_pages` at OpenVirtioBalloon. It is the
// last-read target; callers re-read it from device config to observe
// changes.
NumPages uint32
// Actual is the driver-tracked current balloon size in pages. The
// driver does NOT write this back to device config (the transport
// exposes no device-config write path); it is maintained here as
// Inflate/Deflate succeed. See the package doc's limitation note.
Actual uint32
// NegotiatedFeatures records the driver-feature handshake result.
NegotiatedFeatures uint64
// contains filtered or unexported fields
}
VirtioBalloon wraps one initialised virtio-balloon device.
func OpenVirtioBalloon ¶
func OpenVirtioBalloon(t common.Transport) (*VirtioBalloon, error)
OpenVirtioBalloon drives the full bring-up of one virtio-balloon device:
- Verify the PCI device ID is 0x1045 (modern balloon).
- InitModernConfig walks PCI caps + populates the BAR locators.
- Reset → ACK → DRIVER status progression.
- Read DeviceFeature, require VERSION_1, mask, write DriverFeature.
- Set FEATURES_OK, verify it stuck.
- Allocate + publish inflateq (queue 0) then deflateq (queue 1).
- DRIVER_OK status.
- Read num_pages (le32) from DeviceCfg offset 0.
func (*VirtioBalloon) Deflate ¶
func (v *VirtioBalloon) Deflate(nPages int) error
Deflate reclaims nPages previously-ballooned pages from the host, shrinking the balloon. It posts their page frame numbers to deflateq, drops them from the held set, and decreases Actual by nPages.
nPages must not exceed the number of pages currently held; otherwise ErrDeflateTooMany is returned.
func (*VirtioBalloon) DeflateQueue ¶
func (v *VirtioBalloon) DeflateQueue() *common.Virtqueue
DeflateQueue exposes the deflate virtqueue handle for diagnostics.
func (*VirtioBalloon) Inflate ¶
func (v *VirtioBalloon) Inflate(nPages int) error
Inflate hands nPages guest pages to the host, growing the balloon. It allocates nPages single-page DMA buffers, holds a reference to each so the GC cannot reclaim them while the host owns them, and posts their page frame numbers to inflateq in chunks of at most MaxPFNsPerBuffer. On success Actual is increased by nPages.
nPages must be positive; otherwise ErrZeroPages is returned.
func (*VirtioBalloon) InflateQueue ¶
func (v *VirtioBalloon) InflateQueue() *common.Virtqueue
InflateQueue exposes the inflate virtqueue handle for diagnostics.