balloon.rs0.00%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
use std::fmt::Debug;16
use std::io::{IoSlice, IoSliceMut};17
use std::sync::Arc;18
use std::sync::mpsc::Receiver;19
use std::thread::JoinHandle;20
21
use alioth_macros::Layout;22
use libc::{_SC_PAGESIZE, sysconf};23
use mio::Registry;24
use mio::event::Event;25
use parking_lot::RwLock;26
use serde::Deserialize;27
use serde_aco::Help;28
use zerocopy::{FromBytes, Immutable, IntoBytes};29
30
use crate::hv::IoeventFd;31
use crate::mem::emulated::{Action, Mmio};32
use crate::mem::mapped::{Ram, RamBus};33
use crate::sync::notifier::Notifier;34
use crate::virtio::dev::{DevParam, DeviceId, Virtio, WakeEvent};35
use crate::virtio::queue::{QueueReg, Status, VirtQueue};36
use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};37
use crate::virtio::{FEATURE_BUILT_IN, IrqSender, Result};38
use crate::{bitflags, consts, ffi, impl_mmio_for_zerocopy, mem};39
40
#[repr(C, align(8))]41
#[derive(Debug, Clone, Default, FromBytes, IntoBytes, Immutable, Layout)]42
pub struct BalloonConfig {43
num_pages: u32,44
actual: u32,45
free_page_hint_cmd_id: u32,46
poison_val: u32,47
}48
49
impl_mmio_for_zerocopy!(BalloonConfig);50
51
#[derive(Debug)]52
pub struct BalloonConfigMmio {53
name: Arc<str>,54
config: RwLock<BalloonConfig>,55
}56
57
impl Mmio for BalloonConfigMmio {58
fn size(&self) -> u64 {59
size_of::<BalloonConfig>() as u6460
}61
62
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {63
let config = self.config.read();64
Mmio::read(&*config, offset, size)65
}66
67
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {68
let config = &mut *self.config.write();69
match (offset as usize, size as usize) {70
BalloonConfig::LAYOUT_ACTUAL => {71
config.actual = val as u32;72
log::info!(73
"{}: update: num_pages = {:#x}, actual = {val:#x}",74
self.name,75
config.num_pages,76
);77
Ok(Action::None)78
}79
_ => Mmio::write(config, offset, size, val),80
}81
}82
}83
84
bitflags! {85
pub struct BalloonFeature(u128) {86
MUST_TELL_HOST = 1 << 0;87
STATS_VQ = 1 << 1;88
DEFLATE_ON_OOM = 1 << 2;89
FREE_PAGE_HINT = 1 << 3;90
PAGE_POISON = 1 << 4;91
PAGE_REPORTING = 1 << 5;92
}93
}94
95
consts! {96
pub struct BalloonStats(u16) {97
SWAP_IN = 0;98
SWAP_OUT = 1;99
MAJFLT = 2;100
MINFLT = 3;101
MEMFREE = 4;102
MEMTOT = 5;103
AVAIL = 6;104
CACHES = 7;105
HTLB_PGALLOC = 8;106
HTLB_PGFAIL = 9;107
}108
}109
110
#[derive(Debug, Clone, Copy)]111
enum BalloonQueue {112
Inflate,113
Deflate,114
Stats,115
FreePage,116
Reporting,117
NotExist,118
}119
120
#[derive(Debug)]121
pub struct Balloon {122
name: Arc<str>,123
config: Arc<BalloonConfigMmio>,124
feature: BalloonFeature,125
queues: [BalloonQueue; 5],126
}127
128
impl Balloon {129
pub fn new(param: BalloonParam, name: impl Into<Arc<str>>) -> Result<Self> {130
if unsafe { sysconf(_SC_PAGESIZE) } != 1 << 12 {131
let err = std::io::ErrorKind::Unsupported;132
Err(std::io::Error::from(err))?;133
}134
let config = BalloonConfig {135
num_pages: 0,136
..Default::default()137
};138
let mut feature = BalloonFeature::all();139
if !param.free_page_reporting {140
feature.remove(BalloonFeature::PAGE_REPORTING);141
};142
let name = name.into();143
Ok(Balloon {144
name: name.clone(),145
config: Arc::new(BalloonConfigMmio {146
config: RwLock::new(config),147
name,148
}),149
feature,150
queues: [BalloonQueue::NotExist; 5],151
})152
}153
154
fn inflate(&self, desc: &[IoSlice], ram: &Ram) {155
for buf in desc {156
for bytes in buf.chunks(size_of::<u32>()) {157
let Ok(page_num) = u32::read_from_bytes(bytes) else {158
log::error!(159
"{}: inflate: invalid page_num bytes: {bytes:02x?}",160
self.name161
);162
continue;163
};164
let gpa = (page_num as u64) << 12;165
if let Err(e) = ram.madvise(gpa, 1 << 12, libc::MADV_DONTNEED) {166
log::error!("{}: inflate at GPA {gpa:#x}: {e:?}", self.name);167
} else {168
log::trace!("{}: freed GPA {gpa:#x}", self.name);169
}170
}171
}172
}173
174
fn free_reporting(&self, desc: &mut [IoSliceMut]) {175
for buf in desc.iter_mut() {176
let addr = buf.as_mut_ptr();177
let len = buf.len();178
let ret = ffi!(unsafe { libc::madvise(addr as _, len, libc::MADV_DONTNEED) });179
if let Err(e) = ret {180
log::error!("freeing pages: {addr:p} {len:#x}: {e:?}");181
} else {182
log::trace!("freed pages: {addr:p} {len:#x}");183
}184
}185
}186
}187
188
impl Virtio for Balloon {189
type Config = BalloonConfigMmio;190
type Feature = BalloonFeature;191
192
fn id(&self) -> DeviceId {193
DeviceId::BALLOON194
}195
196
fn name(&self) -> &str {197
&self.name198
}199
200
fn spawn_worker<S, E>(201
self,202
event_rx: Receiver<WakeEvent<S, E>>,203
memory: Arc<RamBus>,204
queue_regs: Arc<[QueueReg]>,205
) -> Result<(JoinHandle<()>, Arc<Notifier>)>206
where207
S: IrqSender,208
E: IoeventFd,209
{210
Mio::spawn_worker(self, event_rx, memory, queue_regs)211
}212
213
fn num_queues(&self) -> u16 {214
self.queues.len() as u16215
}216
217
fn config(&self) -> Arc<BalloonConfigMmio> {218
self.config.clone()219
}220
221
fn feature(&self) -> u128 {222
FEATURE_BUILT_IN | self.feature.bits()223
}224
}225
226
impl VirtioMio for Balloon {227
fn activate<'m, Q, S, E>(228
&mut self,229
feature: u128,230
_active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,231
) -> Result<()>232
where233
Q: VirtQueue<'m>,234
S: IrqSender,235
E: IoeventFd,236
{237
let feature = BalloonFeature::from_bits_retain(feature);238
self.queues[0] = BalloonQueue::Inflate;239
self.queues[1] = BalloonQueue::Deflate;240
let mut index = 2;241
if feature.contains(BalloonFeature::STATS_VQ) {242
self.queues[index] = BalloonQueue::Stats;243
index += 1;244
}245
if feature.contains(BalloonFeature::FREE_PAGE_HINT) {246
self.queues[index] = BalloonQueue::FreePage;247
index += 1;248
}249
if feature.contains(BalloonFeature::PAGE_REPORTING) {250
self.queues[index] = BalloonQueue::Reporting;251
}252
Ok(())253
}254
255
fn handle_queue<'m, Q, S, E>(256
&mut self,257
index: u16,258
active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,259
) -> Result<()>260
where261
Q: VirtQueue<'m>,262
S: IrqSender,263
E: IoeventFd,264
{265
let Some(Some(queue)) = active_mio.queues.get_mut(index as usize) else {266
log::error!("{}: invalid queue index {index}", self.name);267
return Ok(());268
};269
let Some(&ballon_q) = self.queues.get(index as usize) else {270
log::error!("{}: invalid queue index {index}", self.name);271
return Ok(());272
};273
match ballon_q {274
BalloonQueue::Stats => {275
log::info!("{}: VQ_STATES available", self.name);276
return Ok(());277
}278
BalloonQueue::FreePage => {279
log::info!("{}: VQ_FREE_PAGE available", self.name);280
return Ok(());281
}282
_ => {}283
};284
queue.handle_desc(index, active_mio.irq_sender, |chain| {285
match ballon_q {286
BalloonQueue::Inflate => self.inflate(&chain.readable, active_mio.mem),287
BalloonQueue::Deflate => {288
log::info!("{}: VQ_DEFLATE available", self.name);289
}290
BalloonQueue::Reporting => self.free_reporting(&mut chain.writable),291
BalloonQueue::Stats | BalloonQueue::FreePage => todo!(),292
BalloonQueue::NotExist => log::error!("{}: invalid queue index {index}", self.name),293
}294
Ok(Status::Done { len: 0 })295
})296
}297
298
fn handle_event<'a, 'm, Q, S, E>(299
&mut self,300
_event: &Event,301
_active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,302
) -> Result<()>303
where304
Q: VirtQueue<'m>,305
S: IrqSender,306
E: IoeventFd,307
{308
Ok(())309
}310
311
fn reset(&mut self, _registry: &Registry) {312
self.queues = [BalloonQueue::NotExist; 5];313
}314
}315
316
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Help)]317
pub struct BalloonParam {318
/// Enable free page reporting. [default: false]319
#[serde(default)]320
pub free_page_reporting: bool,321
}322
323
impl DevParam for BalloonParam {324
type Device = Balloon;325
326
fn build(self, name: impl Into<Arc<str>>) -> Result<Self::Device> {327
Balloon::new(self, name)328
}329
}330