Skip to content

Commit

Permalink
Merge pull request tock#4283 from alistair23/alistair/garbage-collection
Browse files Browse the repository at this point in the history
capsules: kv: Expose garbage_collection to userspace
  • Loading branch information
lschuermann authored Jan 8, 2025
2 parents 2533a6c + f58d250 commit 9868ada
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 3 deletions.
30 changes: 28 additions & 2 deletions capsules/extra/src/kv_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ enum UserSpaceOp {
Delete,
Add,
Update,
GarbageCollect,
}

/// Contents of the grant for each app.
Expand Down Expand Up @@ -266,6 +267,11 @@ impl<'a, V: kv::KVPermissions<'a>> KVStoreDriver<'a, V> {
return e;
}
}
Some(UserSpaceOp::GarbageCollect) => {
self.kv.garbage_collect()?;
return Ok(());
}

_ => {}
}

Expand Down Expand Up @@ -465,6 +471,24 @@ impl<'a, V: kv::KVPermissions<'a>> kv::KVClient for KVStoreDriver<'a, V> {
self.processid.clear();
self.check_queue();
}

fn garbage_collection_complete(&self, result: Result<(), ErrorCode>) {
self.processid.map(move |id| {
self.apps.enter(id, move |app, upcalls| {
if app.op.contains(&UserSpaceOp::GarbageCollect) {
app.op.clear();
upcalls
.schedule_upcall(upcalls::VALUE, (errorcode::into_statuscode(result), 0, 0))
.ok();
}
})
});

// We have completed the operation so see if there is a queued operation
// to run next.
self.processid.clear();
self.check_queue();
}
}

impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
Expand All @@ -479,8 +503,8 @@ impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
// check if present
0 => CommandReturn::success(),

// get, set, delete, add, update
1 | 2 | 3 | 4 | 5 => {
// get, set, delete, add, update, garbage collect
1 | 2 | 3 | 4 | 5 | 6 => {
if self.processid.is_none() {
// Nothing is using the KV store, so we can handle this
// request.
Expand All @@ -491,6 +515,7 @@ impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
3 => app.op.set(UserSpaceOp::Delete),
4 => app.op.set(UserSpaceOp::Add),
5 => app.op.set(UserSpaceOp::Update),
6 => app.op.set(UserSpaceOp::GarbageCollect),
_ => {}
});
let ret = self.run();
Expand Down Expand Up @@ -520,6 +545,7 @@ impl<'a, V: kv::KVPermissions<'a>> SyscallDriver for KVStoreDriver<'a, V> {
3 => app.op.set(UserSpaceOp::Delete),
4 => app.op.set(UserSpaceOp::Add),
5 => app.op.set(UserSpaceOp::Update),
6 => app.op.set(UserSpaceOp::GarbageCollect),
_ => {}
}
CommandReturn::success()
Expand Down
18 changes: 18 additions & 0 deletions capsules/extra/src/kv_store_permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enum Operation {
Add,
Update,
Delete,
GarbageCollect,
}

/// Current version of the Tock K-V header.
Expand Down Expand Up @@ -288,6 +289,16 @@ impl<'a, K: kv::KV<'a>> kv::KVPermissions<'a> for KVStorePermissions<'a, K> {
}
}

fn garbage_collect(&self) -> Result<(), ErrorCode> {
if self.operation.is_some() {
return Err(ErrorCode::BUSY);
}

self.operation.set(Operation::GarbageCollect);

self.kv.garbage_collect()
}

fn header_size(&self) -> usize {
HEADER_LENGTH
}
Expand Down Expand Up @@ -503,4 +514,11 @@ impl<'a, K: kv::KV<'a>> kv::KVClient for KVStorePermissions<'a, K> {
cb.delete_complete(result, key);
});
}

fn garbage_collection_complete(&self, result: Result<(), ErrorCode>) {
self.operation.clear();
self.client.map(move |cb| {
cb.garbage_collection_complete(result);
});
}
}
27 changes: 26 additions & 1 deletion capsules/extra/src/tickv_kv_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enum Operation {
Add,
Update,
Delete,
GarbageCollect,
}

/// `TicKVKVStore` implements the KV interface using the TicKV KVSystem
Expand Down Expand Up @@ -209,6 +210,21 @@ impl<'a, K: KVSystem<'a, K = T>, T: KeyType> kv::KV<'a> for TicKVKVStore<'a, K,
None => Err((key, ErrorCode::FAIL)),
}
}

fn garbage_collect(&self) -> Result<(), ErrorCode> {
if self.operation.is_some() {
return Err(ErrorCode::BUSY);
}

self.operation.set(Operation::GarbageCollect);

if let Err(e) = self.kv.garbage_collect() {
self.operation.clear();
Err(e)
} else {
Ok(())
}
}
}

impl<'a, K: KVSystem<'a, K = T>, T: KeyType> KVSystemClient<T> for TicKVKVStore<'a, K, T> {
Expand Down Expand Up @@ -260,6 +276,7 @@ impl<'a, K: KVSystem<'a, K = T>, T: KeyType> KVSystemClient<T> for TicKVKVStore<
cb.delete_complete(Err(ErrorCode::FAIL), unhashed_key);
});
}
Operation::GarbageCollect => {}
}
} else {
match op {
Expand Down Expand Up @@ -350,6 +367,7 @@ impl<'a, K: KVSystem<'a, K = T>, T: KeyType> KVSystemClient<T> for TicKVKVStore<
}
};
}
Operation::GarbageCollect => {}
}
}
});
Expand Down Expand Up @@ -441,6 +459,7 @@ impl<'a, K: KVSystem<'a, K = T>, T: KeyType> KVSystemClient<T> for TicKVKVStore<
});
});
}
Operation::GarbageCollect => {}
});
}

Expand Down Expand Up @@ -564,8 +583,14 @@ impl<'a, K: KVSystem<'a, K = T>, T: KeyType> KVSystemClient<T> for TicKVKVStore<
});
});
}
Operation::GarbageCollect => {}
});
}

fn garbage_collect_complete(&self, _result: Result<(), ErrorCode>) {}
fn garbage_collect_complete(&self, result: Result<(), ErrorCode>) {
self.operation.clear();
self.client.map(move |cb| {
cb.garbage_collection_complete(result);
});
}
}
45 changes: 45 additions & 0 deletions capsules/extra/src/virtual_kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum Operation {
Delete,
Add,
Update,
GarbageCollect,
}

pub struct VirtualKVPermissions<'a, V: kv::KVPermissions<'a>> {
Expand Down Expand Up @@ -224,6 +225,16 @@ impl<'a, V: kv::KVPermissions<'a>> kv::KVPermissions<'a> for VirtualKVPermission
.map_err(|e| (self.key.take().unwrap(), e))
}

fn garbage_collect(&self) -> Result<(), ErrorCode> {
if self.operation.is_some() {
return Err(ErrorCode::BUSY);
}

self.operation.set(Operation::GarbageCollect);

self.mux_kv.do_next_op(false)
}

fn header_size(&self) -> usize {
self.mux_kv.kv.header_size()
}
Expand All @@ -250,6 +261,28 @@ impl<'a, V: kv::KVPermissions<'a>> MuxKVPermissions<'a, V> {

mnode.map_or(Ok(()), |node| {
node.operation.map_or(Ok(()), |op| {
// GarbageCollect doesn't have a key, so we check it above
// the match case below
if op == Operation::GarbageCollect {
return match self.kv.garbage_collect() {
Ok(()) => {
self.inflight.set(node);
Ok(())
}
Err(e) => {
node.operation.clear();
if async_op {
node.client.map(move |cb| {
cb.garbage_collection_complete(Err(e));
});
Ok(())
} else {
Err(e)
}
}
};
}

node.key.take().map_or(Ok(()), |key| match op {
Operation::Get => node.value.take().map_or(Ok(()), |value| {
node.valid_ids.map_or(Ok(()), |perms| {
Expand Down Expand Up @@ -364,6 +397,7 @@ impl<'a, V: kv::KVPermissions<'a>> MuxKVPermissions<'a, V> {
}
})
}
Operation::GarbageCollect => Err(ErrorCode::NOSUPPORT),
})
})
})
Expand Down Expand Up @@ -445,4 +479,15 @@ impl<'a, V: kv::KVPermissions<'a>> kv::KVClient for MuxKVPermissions<'a, V> {

let _ = self.do_next_op(true);
}

fn garbage_collection_complete(&self, result: Result<(), ErrorCode>) {
self.inflight.take().map(|node| {
node.operation.clear();
node.client.map(move |cb| {
cb.garbage_collection_complete(result);
});
});

let _ = self.do_next_op(true);
}
}
38 changes: 38 additions & 0 deletions kernel/src/hil/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ pub trait KVClient {
/// completed.
/// - `key`: The key buffer.
fn delete_complete(&self, result: Result<(), ErrorCode>, key: SubSliceMut<'static, u8>);

/// This callback is called when the garbage collection operation completes.
///
/// ### Return Values
///
/// - `result`: `Ok(())` on success, `Err(ErrorCode)` on error. Valid
/// `ErrorCode`s:
/// - `FAIL`: An internal error occurred and the operation cannot be
/// completed.
fn garbage_collection_complete(&self, result: Result<(), ErrorCode>);
}

/// Key-Value interface with permissions.
Expand Down Expand Up @@ -319,6 +329,20 @@ pub trait KVPermissions<'a> {
permissions: StoragePermissions,
) -> Result<(), (SubSliceMut<'static, u8>, ErrorCode)>;

/// Run garbage collection on the underlying Key/Value store.
///
/// This is generally used to reclaim keys that have been removed with
/// the `delete()` call.
///
/// ### Return
///
/// - On success returns `Ok(())`. A callback will be issued.
/// - On error, returns the buffers and:
/// - `BUSY`: An operation is already in progress.
/// - `FAIL`: An internal error occurred and the operation cannot be
/// completed.
fn garbage_collect(&self) -> Result<(), ErrorCode>;

/// Returns the length of the key-value store's header in bytes.
///
/// Room for this header must be accommodated in a `set`, `add`, or `update`
Expand Down Expand Up @@ -478,4 +502,18 @@ pub trait KV<'a> {
&self,
key: SubSliceMut<'static, u8>,
) -> Result<(), (SubSliceMut<'static, u8>, ErrorCode)>;

/// Run garbage collection on the underlying Key/Value store.
///
/// This is generally used to reclaim keys that have been removed with
/// the `delete()` call.
///
/// ### Return
///
/// - On success returns `Ok(())`. A callback will be issued.
/// - On error, returns the buffers and:
/// - `BUSY`: An operation is already in progress.
/// - `FAIL`: An internal error occurred and the operation cannot be
/// completed.
fn garbage_collect(&self) -> Result<(), ErrorCode>;
}

0 comments on commit 9868ada

Please sign in to comment.