Skip to content

Commit dd9aa47

Browse files
mayastor-borsdsharma-dc
andcommitted
Merge #1848
1848: feat(encryption): allow pools on same node to share keys r=dsharma-dc a=dsharma-dc The pools in same node(io-engine) can now share encryption key. Earlier, we used to set a crypto vbdev as key owner which means that during crypto vbdev destroy, the key was also destroyed. Now we don't set a crypto vbdev as owner and hence need to manage the key lifecycle using a hashmap that keeps track of which key is being used by which all crypto vbdevs. Co-authored-by: Diwakar Sharma <[email protected]>
2 parents 4a81b94 + a7a0757 commit dd9aa47

File tree

2 files changed

+136
-16
lines changed

2 files changed

+136
-16
lines changed

io-engine/src/bdev/crypto.rs

Lines changed: 132 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use spdk_rs::{
2424
accel_dpdk_cryptodev_enable, accel_dpdk_cryptodev_set_driver, create_crypto_disk,
2525
create_crypto_opts_by_name, delete_crypto_disk, spdk_accel_assign_opc,
2626
spdk_accel_crypto_key, spdk_accel_crypto_key_create, spdk_accel_crypto_key_create_param,
27-
spdk_accel_crypto_key_get,
27+
spdk_accel_crypto_key_destroy, spdk_accel_crypto_key_get,
2828
},
2929
};
3030
use std::{
@@ -38,6 +38,91 @@ use crate::{
3838
ffihelper::{cb_arg, pair, FfiResult},
3939
};
4040

41+
use once_cell::sync::Lazy;
42+
use std::{
43+
collections::{HashMap, HashSet},
44+
sync::RwLock,
45+
};
46+
47+
/// Global synchronized HashMap (Crypto Key -> Set of crypto vbdevs).
48+
/// Since this doesn't lie in IO path, not using per-bucket locks in hashmap.
49+
static KEY_CRYPTO_VBDEV_MAP: Lazy<RwLock<HashMap<String, HashSet<String>>>> =
50+
Lazy::new(|| RwLock::new(HashMap::new()));
51+
52+
/// Adds a dependency ('crypto_vbdev_name' uses 'key_name')
53+
fn add_key_user(
54+
key_params: &EncryptionKey,
55+
crypto_vbdev_name: String,
56+
) -> Result<*mut spdk_accel_crypto_key, BdevError> {
57+
let mut map = KEY_CRYPTO_VBDEV_MAP.write().unwrap();
58+
59+
// Create the key first.
60+
let key = create_crypto_key(key_params)?;
61+
62+
map.entry(key_params.key_name.clone())
63+
.or_default()
64+
.insert(crypto_vbdev_name);
65+
Ok(key)
66+
}
67+
68+
/// Removes a dependency ('crypto_vbdev_name' stops using 'key_name')
69+
fn remove_key_user(key_name: &str, crypto_vbdev_name: String) {
70+
let mut map = KEY_CRYPTO_VBDEV_MAP.write().unwrap();
71+
if let Some(users) = map.get_mut(key_name) {
72+
users.remove(&crypto_vbdev_name);
73+
// Clean up empty sets immediately
74+
if users.is_empty() {
75+
// delete the key in spdk.
76+
unsafe {
77+
let key = spdk_accel_crypto_key_get(
78+
key_name.to_string().into_cstring().as_ptr() as *mut c_char
79+
);
80+
if key.is_null() {
81+
warn!("Non-existent key in SPDK.");
82+
} else {
83+
let err = spdk_accel_crypto_key_destroy(key);
84+
if err != 0 {
85+
error!("Failed to destroy spdk accel crypto key: {err}");
86+
// Return and don't remove from the map yet.
87+
return;
88+
}
89+
}
90+
}
91+
map.remove(key_name);
92+
}
93+
}
94+
}
95+
96+
/// Gets all crypto vbdev names using a given key_name.
97+
fn get_users_of(key_name: &str) -> Option<HashSet<String>> {
98+
let map = KEY_CRYPTO_VBDEV_MAP.read().unwrap();
99+
// Clone or return ref perhaps?
100+
map.get(key_name).cloned()
101+
}
102+
103+
/// Reverse lookup: O(n * m)
104+
/// Finds which hash key contains a particular crypto vbdev name (scans the map). Should be
105+
/// ok to bear this cost as there won't be exhorbitant number of pool or keys per io-engine.
106+
fn find_key_for_crypto_vbdev(crypto_vbdev_name: &str) -> Option<String> {
107+
let map = KEY_CRYPTO_VBDEV_MAP.read().unwrap();
108+
map.iter().find_map(|(key, users)| {
109+
if users.contains(crypto_vbdev_name) {
110+
Some(key.to_owned())
111+
} else {
112+
None
113+
}
114+
})
115+
}
116+
117+
/// Purges all key entries that have no crypto vbdev users left. This will not be usually
118+
/// needed because remove call is taking care of deleting the key entry upon removal of
119+
/// last crypto vbdev user.
120+
#[allow(dead_code)]
121+
fn purge_empty_keys() {
122+
let mut map = KEY_CRYPTO_VBDEV_MAP.write().unwrap();
123+
map.retain(|_, users| !users.is_empty());
124+
}
125+
41126
/// Representation of a crypto vbdev to be created in SPDK.
42127
/// TODO: Currently we don't fit crypto vbdev into the uri based bdev management
43128
/// as the information required to completely setup the crypto vbdev isn't part of the uri,
@@ -200,6 +285,12 @@ fn create_crypto_key(key_params: &EncryptionKey) -> Result<*mut spdk_accel_crypt
200285
};
201286

202287
unsafe {
288+
// Check if the key with this name exists. If it does, then we reuse that.
289+
let existing_key = spdk_accel_crypto_key_get(create_key_params.key_name);
290+
if !existing_key.is_null() {
291+
info!("Key {:?} exists already, reusing it.", key_params.key_name);
292+
return Ok(existing_key);
293+
};
203294
let ret = spdk_accel_crypto_key_create(&create_key_params);
204295
if ret != 0 {
205296
Err(BdevError::CreateBdevFailed {
@@ -222,7 +313,10 @@ extern "C" fn crypto_vbdev_op_cb(arg: *mut std::os::raw::c_void, errno: i32) {
222313
}
223314

224315
/// Destroy a crypto vbdev.
225-
pub async fn destroy_crypto_vbdev(vbdev_name: String) -> Result<(), BdevError> {
316+
pub async fn destroy_crypto_vbdev(
317+
vbdev_name: String,
318+
key_name: Option<String>,
319+
) -> Result<(), BdevError> {
226320
let (s, r) = pair::<i32>();
227321
unsafe {
228322
delete_crypto_disk(
@@ -232,14 +326,34 @@ pub async fn destroy_crypto_vbdev(vbdev_name: String) -> Result<(), BdevError> {
232326
);
233327
}
234328

235-
r.await
329+
let ret = r
330+
.await
236331
.expect("callback gone while deleting crypto disk")
237332
.to_result(|e| BdevError::DestroyBdevFailed {
238333
source: Errno::from_raw(e),
239334
name: vbdev_name.to_string(),
240-
})?;
335+
});
336+
if let Err(e) = ret {
337+
match e {
338+
BdevError::BdevNotFound { .. } => {}
339+
_else => return Err(_else),
340+
}
341+
}
241342

242343
info!("crypto vbdev {vbdev_name} destroyed successfully");
344+
345+
let key_name = match key_name.or_else(|| find_key_for_crypto_vbdev(&vbdev_name)) {
346+
Some(k) => k,
347+
None => {
348+
warn!("Mapped key not found for {vbdev_name}");
349+
return Ok(());
350+
}
351+
};
352+
353+
remove_key_user(key_name.as_str(), vbdev_name);
354+
if let Some(users) = get_users_of(key_name.as_str()) {
355+
trace!("[remove] Remaining key users {key_name}: {users:?}");
356+
}
243357
Ok(())
244358
}
245359
/// The primary function to create a crypto vbdev in spdk.
@@ -248,29 +362,35 @@ pub fn create_crypto_vbdev_on_base_bdev(
248362
base_bdev_name: &str,
249363
key_params: &EncryptionKey,
250364
) -> Result<(), BdevError> {
251-
// Create the key.
252-
let key = create_crypto_key(key_params)?;
365+
// Create the key and add to the map.
366+
let key = add_key_user(key_params, crypto_vbdev_name.to_string())?;
367+
if let Some(users) = get_users_of(key_params.key_name.as_str()) {
368+
trace!(
369+
"[add] updated key users {:?}: {users:?}",
370+
key_params.key_name
371+
);
372+
}
253373

254374
// Setup the crypto opts using the key now.
255375
let crypto_opts_ptr = unsafe {
256376
create_crypto_opts_by_name(
257377
crypto_vbdev_name.into_cstring().as_ptr() as *mut c_char,
258378
base_bdev_name.into_cstring().as_ptr() as *mut c_char,
259379
key,
260-
true,
380+
false,
261381
)
262382
};
263383

264384
// Now create the crypto vbdev.
265385
let ret = unsafe { create_crypto_disk(crypto_opts_ptr) };
266386

267387
if ret != 0 {
268-
Err(BdevError::CreateBdevFailed {
388+
return Err(BdevError::CreateBdevFailed {
269389
source: Errno::from_raw(ret),
270390
name: crypto_vbdev_name.to_string(),
271-
})
272-
} else {
273-
info!("crypto vbdev {crypto_vbdev_name} created on base bdev {base_bdev_name}");
274-
Ok(())
391+
});
275392
}
393+
info!("crypto vbdev {crypto_vbdev_name} created on base bdev {base_bdev_name}");
394+
395+
Ok(())
276396
}

io-engine/src/lvs/lvs_store.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ impl Lvs {
627627
..
628628
}) => {
629629
let cbdev_name: Option<String> = args.crypto_vbdev_name.clone();
630+
let key_name = args.enc_key.as_ref().map(|e| e.key_name.clone());
630631
match Self::create_from_args_inner(PoolArgs {
631632
disks: vec![bdev_name.clone()],
632633
..args
@@ -636,7 +637,7 @@ impl Lvs {
636637
Err(create) => {
637638
// destroy crypto vbdev first.
638639
if let Some(c) = cbdev_name.as_ref() {
639-
let _ = destroy_crypto_vbdev(c.clone()).await.map_err(|e| {
640+
let _ = destroy_crypto_vbdev(c.clone(), key_name).await.map_err(|e| {
640641
error!(
641642
"failed to delete crypto vbdev {c} after failed pool creation. {e}"
642643
);
@@ -696,7 +697,7 @@ impl Lvs {
696697
if base_bdev.driver() == "crypto" {
697698
let cbdev = base_bdev.crypto_base_bdev();
698699

699-
if let Err(e) = destroy_crypto_vbdev(base_bdev.name().to_string()).await {
700+
if let Err(e) = destroy_crypto_vbdev(base_bdev.name().to_string(), None).await {
700701
error!(
701702
"failed to delete crypto vbdev {:?} during lvs export. {e}",
702703
base_bdev.name()
@@ -805,8 +806,7 @@ impl Lvs {
805806
// If the base_bdev is a crypto vbdev then we need to destroy both - the crypto vbdev and it's base.
806807
if base_bdev.driver() == "crypto" {
807808
let cbdev = base_bdev.crypto_base_bdev();
808-
809-
let _ = destroy_crypto_vbdev(base_bdev.name().to_string())
809+
let _ = destroy_crypto_vbdev(base_bdev.name().to_string(), None)
810810
.await
811811
.map_err(|e| {
812812
error!(

0 commit comments

Comments
 (0)