Skip to content

Commit d2a3374

Browse files
mayastor-borshrudaya21
andcommitted
Merge #1500
1500: fix(snapshot): reset usage cache for the parent lvol and successor lvol when snapshot is destroyed (Cherrypick PR) r=hrudaya21 a=hrudaya21 Co-authored-by: Hrudaya <[email protected]>
2 parents ca52ddd + bf26497 commit d2a3374

File tree

5 files changed

+314
-7
lines changed

5 files changed

+314
-7
lines changed

io-engine/src/core/snapshot.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,14 @@ pub trait SnapshotOps {
349349
&self,
350350
total_ancestor_snap_size: u64,
351351
) -> Option<u64>;
352+
353+
/// When snapshot is destroyed, reset the parent lvol usage cache and its
354+
/// successor snapshot and clone usage cache.
355+
fn reset_snapshot_parent_successor_usage_cache(&self);
356+
357+
/// When snapshot is destroyed, reset cache of successor snapshots and
358+
/// clones based on snapshot parent uuid.
359+
fn reset_successor_lvol_usage_cache(&self, snapshot_parent_uuid: String);
352360
}
353361

354362
/// Traits gives the Snapshots Related Parameters.

io-engine/src/lvs/lvol_snapshot.rs

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use futures::channel::oneshot;
1919
use nix::errno::Errno;
2020
use spdk_rs::libspdk::{
2121
spdk_blob,
22+
spdk_blob_reset_used_clusters_cache,
2223
spdk_lvol,
2324
spdk_xattr_descriptor,
2425
vbdev_lvol_create_clone_ext,
@@ -562,6 +563,7 @@ impl SnapshotOps for Lvol {
562563
/// Destroy snapshot.
563564
async fn destroy_snapshot(mut self) -> Result<(), Self::Error> {
564565
if self.list_clones_by_snapshot_uuid().is_empty() {
566+
self.reset_snapshot_parent_successor_usage_cache();
565567
self.destroy().await?;
566568
} else {
567569
self.set_blob_attr(
@@ -731,7 +733,7 @@ impl SnapshotOps for Lvol {
731733
let Some(bdev) = UntypedBdev::bdev_first() else {
732734
return; /* No devices available */
733735
};
734-
let futures = bdev
736+
let snap_list = bdev
735737
.into_iter()
736738
.filter(|b| b.driver() == "lvol")
737739
.map(|b| Lvol::try_from(b).unwrap())
@@ -740,8 +742,11 @@ impl SnapshotOps for Lvol {
740742
&& b.is_discarded_snapshot()
741743
&& b.list_clones_by_snapshot_uuid().is_empty()
742744
})
743-
.map(|s| s.destroy())
744-
.collect::<Vec<_>>();
745+
.collect::<Vec<Lvol>>();
746+
snap_list
747+
.iter()
748+
.for_each(|s| s.reset_snapshot_parent_successor_usage_cache());
749+
let futures = snap_list.into_iter().map(|s| s.destroy());
745750
let result = join_all(futures).await;
746751
for r in result {
747752
match r {
@@ -792,4 +797,68 @@ impl SnapshotOps for Lvol {
792797
None
793798
}
794799
}
800+
/// When snapshot is destroyed, reset the parent lvol usage cache and its
801+
/// successor snapshot and clone usage cache.
802+
fn reset_snapshot_parent_successor_usage_cache(&self) {
803+
if let Some(snapshot_parent_uuid) =
804+
Lvol::get_blob_xattr(self, SnapshotXattrs::ParentId.name())
805+
{
806+
if let Some(bdev) =
807+
UntypedBdev::lookup_by_uuid_str(snapshot_parent_uuid.as_str())
808+
{
809+
if let Ok(parent_lvol) = Lvol::try_from(bdev) {
810+
unsafe {
811+
spdk_blob_reset_used_clusters_cache(
812+
parent_lvol.blob_checked(),
813+
);
814+
}
815+
}
816+
}
817+
self.reset_successor_lvol_usage_cache(snapshot_parent_uuid);
818+
}
819+
}
820+
821+
/// When snapshot is destroyed, reset cache of successor snapshots and
822+
/// clones based on snapshot parent uuid.
823+
fn reset_successor_lvol_usage_cache(&self, snapshot_parent_uuid: String) {
824+
let mut successor_snapshots = Lvol::list_all_snapshots()
825+
.iter()
826+
.map(|v| v.snapshot_lvol())
827+
.filter_map(|l| {
828+
let uuid =
829+
Lvol::get_blob_xattr(self, SnapshotXattrs::ParentId.name());
830+
match uuid {
831+
Some(uuid) if uuid == snapshot_parent_uuid => {
832+
Some(l.clone())
833+
}
834+
_ => None,
835+
}
836+
})
837+
.collect::<Vec<Lvol>>();
838+
let mut successor_clones: Vec<Lvol> = vec![];
839+
840+
while !successor_snapshots.is_empty() || !successor_clones.is_empty() {
841+
if let Some(snapshot) = successor_snapshots.pop() {
842+
unsafe {
843+
spdk_blob_reset_used_clusters_cache(
844+
snapshot.blob_checked(),
845+
);
846+
}
847+
let new_clone_list = snapshot.list_clones_by_snapshot_uuid();
848+
successor_clones.extend(new_clone_list);
849+
}
850+
851+
if let Some(clone) = successor_clones.pop() {
852+
unsafe {
853+
spdk_blob_reset_used_clusters_cache(clone.blob_checked());
854+
}
855+
let new_snap_list = clone
856+
.list_snapshot_by_source_uuid()
857+
.iter()
858+
.map(|v| v.snapshot_lvol().clone())
859+
.collect::<Vec<Lvol>>();
860+
successor_snapshots.extend(new_snap_list);
861+
}
862+
}
863+
}
795864
}

io-engine/src/lvs/lvs_lvol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,7 @@ impl LvsLvol for Lvol {
10161016
if snapshot_lvol.list_clones_by_snapshot_uuid().is_empty()
10171017
&& snapshot_lvol.is_discarded_snapshot()
10181018
{
1019+
snapshot_lvol.reset_snapshot_parent_successor_usage_cache();
10191020
snapshot_lvol.destroy().await?;
10201021
}
10211022
}

io-engine/tests/snapshot_lvol.rs

Lines changed: 230 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1055,7 +1055,7 @@ async fn test_snapshot_clone() {
10551055
let clones = snapshot_lvol.list_clones_by_snapshot_uuid();
10561056

10571057
assert_eq!(clones.len(), 2, "Number of Clones Doesn't match");
1058-
for clone in clones {
1058+
for clone in &clones {
10591059
assert!(
10601060
clone.is_snapshot_clone().is_some(),
10611061
"Wrongly judge as not a clone"
@@ -1066,6 +1066,10 @@ async fn test_snapshot_clone() {
10661066
snapshot_lvol.is_snapshot_clone().is_none(),
10671067
"Wrongly judge as clone"
10681068
);
1069+
for clone in clones {
1070+
clone.destroy().await.expect("destroy clone failed");
1071+
}
1072+
clean_snapshots(Lvol::list_all_snapshots()).await;
10691073
})
10701074
.await;
10711075
}
@@ -1554,3 +1558,228 @@ async fn test_snapshot_verify_restore_data() {
15541558
// Check the original replica and restore clone is identical.
15551559
validate_replicas(&vec![repl_1.clone(), restore_1.clone()]).await;
15561560
}
1561+
1562+
#[tokio::test]
1563+
async fn test_snapshot_parent_usage_post_snapshot_destroy() {
1564+
let ms = get_ms();
1565+
const LVOL_NAME: &str = "lvol16";
1566+
1567+
ms.spawn(async move {
1568+
// Create a pool and lvol.
1569+
let pool = create_test_pool(
1570+
"pool16",
1571+
"malloc:///disk16?size_mb=128".to_string(),
1572+
)
1573+
.await;
1574+
let lvol = pool
1575+
.create_lvol(
1576+
LVOL_NAME,
1577+
32 * 1024 * 1024,
1578+
Some(&Uuid::new_v4().to_string()),
1579+
true,
1580+
)
1581+
.await
1582+
.expect("Failed to create test lvol");
1583+
let cluster_size = pool.blob_cluster_size();
1584+
// Create a test snapshot.
1585+
let entity_id = String::from("lvol16_e1");
1586+
let parent_id = lvol.uuid();
1587+
let txn_id = Uuid::new_v4().to_string();
1588+
let snap_name = String::from("lvol16_snap1");
1589+
let snapshot_uuid = Uuid::new_v4().to_string();
1590+
1591+
let snapshot_params = SnapshotParams::new(
1592+
Some(entity_id),
1593+
Some(parent_id),
1594+
Some(txn_id),
1595+
Some(snap_name),
1596+
Some(snapshot_uuid),
1597+
Some(Utc::now().to_string()),
1598+
false,
1599+
);
1600+
1601+
bdev_io::write_some(LVOL_NAME, 2 * cluster_size, 16, 0xaau8)
1602+
.await
1603+
.expect("Failed to write data to volume");
1604+
lvol.create_snapshot(snapshot_params.clone())
1605+
.await
1606+
.expect("Failed to create the first snapshot for test volume");
1607+
let snapshot_list = Lvol::list_all_snapshots();
1608+
assert_eq!(1, snapshot_list.len(), "Snapshot Count not matched!!");
1609+
1610+
let snapshot_lvol = UntypedBdev::lookup_by_uuid_str(
1611+
snapshot_list
1612+
.get(0)
1613+
.unwrap()
1614+
.snapshot_params()
1615+
.snapshot_uuid()
1616+
.unwrap_or_default()
1617+
.as_str(),
1618+
)
1619+
.map(|b| Lvol::try_from(b).expect("Can't create Lvol from device"))
1620+
.unwrap();
1621+
assert_eq!(
1622+
lvol.usage().allocated_bytes,
1623+
0,
1624+
"Source Lvol size should be 0, after snapshot created from it"
1625+
);
1626+
1627+
bdev_io::write_some(LVOL_NAME, 3 * cluster_size, 16, 0xbbu8)
1628+
.await
1629+
.expect("Failed to write data to volume");
1630+
bdev_io::write_some(LVOL_NAME, 4 * cluster_size, 16, 0xccu8)
1631+
.await
1632+
.expect("Failed to write data to volume");
1633+
snapshot_lvol
1634+
.destroy()
1635+
.await
1636+
.expect("Destroy snapshot failed");
1637+
assert_eq!(
1638+
lvol.usage().allocated_bytes,
1639+
5 * cluster_size,
1640+
"Source Lvol size should be restored after snapshot destroy"
1641+
);
1642+
})
1643+
.await;
1644+
}
1645+
1646+
#[tokio::test]
1647+
async fn test_clone_snapshot_usage_post_clone_destroy() {
1648+
let ms = get_ms();
1649+
const LVOL_NAME: &str = "lvol17";
1650+
1651+
ms.spawn(async move {
1652+
// Create a pool and lvol.
1653+
let pool = create_test_pool(
1654+
"pool17",
1655+
"malloc:///disk17?size_mb=128".to_string(),
1656+
)
1657+
.await;
1658+
let lvol = pool
1659+
.create_lvol(
1660+
LVOL_NAME,
1661+
32 * 1024 * 1024,
1662+
Some(&Uuid::new_v4().to_string()),
1663+
true,
1664+
)
1665+
.await
1666+
.expect("Failed to create test lvol");
1667+
let cluster_size = pool.blob_cluster_size();
1668+
// Create a test snapshot.
1669+
let entity_id = String::from("lvol17_e1");
1670+
let parent_id = lvol.uuid();
1671+
let txn_id = Uuid::new_v4().to_string();
1672+
let snap_name = String::from("lvol17_snap1");
1673+
let snapshot_uuid = Uuid::new_v4().to_string();
1674+
1675+
let mut snapshot_params = SnapshotParams::new(
1676+
Some(entity_id),
1677+
Some(parent_id),
1678+
Some(txn_id),
1679+
Some(snap_name),
1680+
Some(snapshot_uuid),
1681+
Some(Utc::now().to_string()),
1682+
false,
1683+
);
1684+
1685+
bdev_io::write_some(LVOL_NAME, 2 * cluster_size, 16, 0xaau8)
1686+
.await
1687+
.expect("Failed to write data to volume");
1688+
lvol.create_snapshot(snapshot_params.clone())
1689+
.await
1690+
.expect("Failed to create the first snapshot for test volume");
1691+
let snapshot_list = Lvol::list_all_snapshots();
1692+
assert_eq!(1, snapshot_list.len(), "Snapshot Count not matched!!");
1693+
1694+
let snapshot_lvol = UntypedBdev::lookup_by_uuid_str(
1695+
snapshot_list
1696+
.get(0)
1697+
.unwrap()
1698+
.snapshot_params()
1699+
.snapshot_uuid()
1700+
.unwrap_or_default()
1701+
.as_str(),
1702+
)
1703+
.map(|b| Lvol::try_from(b).expect("Can't create Lvol from device"))
1704+
.unwrap();
1705+
assert_eq!(
1706+
lvol.usage().allocated_bytes,
1707+
0,
1708+
"Source Lvol size should be 0, after snapshot created from it"
1709+
);
1710+
1711+
let clone_name = String::from("lvol17_snap1_clone_1");
1712+
let clone_uuid = Uuid::new_v4().to_string();
1713+
let source_uuid = snapshot_lvol.uuid();
1714+
1715+
let clone_param = CloneParams::new(
1716+
Some(clone_name),
1717+
Some(clone_uuid),
1718+
Some(source_uuid),
1719+
Some(Utc::now().to_string()),
1720+
);
1721+
let clone1 = snapshot_lvol
1722+
.create_clone(clone_param.clone())
1723+
.await
1724+
.expect("Failed to create a clone");
1725+
bdev_io::write_some("lvol17_snap1_clone_1", 0, 16, 0xbbu8)
1726+
.await
1727+
.expect("Failed to write data to volume");
1728+
snapshot_params.set_parent_id(clone1.uuid());
1729+
snapshot_params.set_entity_id(String::from("lvol17_clone1_e1"));
1730+
snapshot_params.set_name(String::from("lvol17_clone_1_snap1"));
1731+
snapshot_params.set_snapshot_uuid(Uuid::new_v4().to_string());
1732+
snapshot_params.set_txn_id(Uuid::new_v4().to_string());
1733+
1734+
clone1
1735+
.create_snapshot(snapshot_params.clone())
1736+
.await
1737+
.expect("Failed to create the first snapshot for test volume");
1738+
snapshot_params.set_parent_id(clone1.uuid());
1739+
snapshot_params.set_entity_id(String::from("lvol17_clone1_e2"));
1740+
snapshot_params.set_name(String::from("lvol17_clone_1_snap2"));
1741+
snapshot_params.set_snapshot_uuid(Uuid::new_v4().to_string());
1742+
snapshot_params.set_txn_id(Uuid::new_v4().to_string());
1743+
bdev_io::write_some(
1744+
"lvol17_snap1_clone_1",
1745+
3 * cluster_size,
1746+
16,
1747+
0xbbu8,
1748+
)
1749+
.await
1750+
.expect("Failed to write data to volume");
1751+
clone1
1752+
.create_snapshot(snapshot_params.clone())
1753+
.await
1754+
.expect("Failed to create the first snapshot for test volume");
1755+
let mut clone_snapshot: Vec<Lvol> = clone1
1756+
.list_snapshot_by_source_uuid()
1757+
.iter()
1758+
.map(|v| v.snapshot_lvol().clone())
1759+
.collect();
1760+
lvol.destroy()
1761+
.await
1762+
.expect("Original replica destroy failed");
1763+
clone1
1764+
.destroy_replica()
1765+
.await
1766+
.expect("Destroy Clone failed");
1767+
assert_eq!(
1768+
clone_snapshot.len(),
1769+
2,
1770+
"Number of Clone Snapshot not matched"
1771+
);
1772+
assert_eq!(
1773+
clone_snapshot.remove(0).usage().allocated_bytes,
1774+
cluster_size,
1775+
"Clone1 snap1 cache is not cleared"
1776+
);
1777+
assert_eq!(
1778+
clone_snapshot.remove(0).usage().allocated_bytes,
1779+
cluster_size,
1780+
"Clone1 snap2 cache is not cleared"
1781+
);
1782+
clean_snapshots(Lvol::list_all_snapshots()).await;
1783+
})
1784+
.await;
1785+
}

nix/pkgs/libspdk/default.nix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ let
5656
# 7. Copy SHA256 from 'got' of the error message to 'sha256' field.
5757
# 8. 'nix-shell' build must now succeed.
5858
drvAttrs = rec {
59-
version = "23.01-716b1ed";
59+
version = "23.01-6c81b80";
6060

6161
src = fetchFromGitHub {
6262
owner = "openebs";
6363
repo = "spdk";
64-
rev = "716b1ed489f1bc17698f7c4e5be347b0b7dc56ca";
65-
sha256 = "sha256-Q9fwW8x4kPNslYjnlG08aZQ7aX8En92BAP6cXG81JaI=";
64+
rev = "6c81b80f1f202160de57924549655813b28f7120";
65+
sha256 = "sha256-Nwxpw25jffQ1srQh+rBNbX6z24YVAdvWg+r7nh2Ku4c=";
6666
fetchSubmodules = true;
6767
};
6868

0 commit comments

Comments
 (0)