vbdev_lvol: external snapshot rpc interface

Add RPC interfaces for creation of esnap clone lvols. This also
exercises esnap clone creation and various operations involving
snapshots and clones of esnap clones to ensure that bdev_get_bdevs
reports state correctly.

Change-Id: Ib87d01026ef6e45203c4d9451759885a7be02d87
Signed-off-by: Mike Gerdts <mgerdts@nvidia.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14978
Reviewed-by: Michal Berger <michal.berger@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Mike Gerdts 2022-01-23 23:15:09 -06:00 committed by David Ko
parent aa209d4ecb
commit 68cde3b770
11 changed files with 1112 additions and 28 deletions

View File

@ -0,0 +1,673 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="181.24mm"
height="79.375mm"
version="1.1"
viewBox="0 0 181.24 79.375"
id="svg172"
sodipodi:docname="lvol_esnap_clone.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<sodipodi:namedview
id="namedview174"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="false"
inkscape:zoom="1.7926966"
inkscape:cx="338.59607"
inkscape:cy="148.93764"
inkscape:window-width="1351"
inkscape:window-height="930"
inkscape:window-x="762"
inkscape:window-y="134"
inkscape:window-maximized="0"
inkscape:current-layer="g170" />
<title
id="title2">Thin Provisioning</title>
<defs
id="defs28">
<marker
id="marker2036"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path4" />
</marker>
<marker
id="marker1960"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path7" />
</marker>
<marker
id="marker1890"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path10" />
</marker>
<marker
id="marker1826"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path13" />
</marker>
<marker
id="marker1816"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path16" />
</marker>
<marker
id="Arrow1Mend"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill-rule="evenodd"
stroke="#000"
stroke-width="1pt"
id="path19" />
</marker>
<marker
id="marker11771-4-9"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill="#f00"
fill-rule="evenodd"
stroke="#ff2a2a"
stroke-width="1pt"
id="path22" />
</marker>
<marker
id="marker1826-2-4-7-1-7"
overflow="visible"
orient="auto">
<path
transform="matrix(-.4 0 0 -.4 -4 0)"
d="m0 0 5-5-17.5 5 17.5 5z"
fill="#00f"
fill-rule="evenodd"
stroke="#00f"
stroke-width="1pt"
id="path25" />
</marker>
</defs>
<metadata
id="metadata30">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>Thin Provisioning</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(2.6458 2.3956)"
id="g34">
<rect
x="-2.6458"
y="-2.3956"
width="181.24"
height="79.375"
fill="#fffffe"
stroke-width=".26458"
id="rect32" />
</g>
<g
transform="translate(-3.9688 -4.6356)"
id="g170">
<g
stroke="#000"
id="g52">
<g
stroke-width=".26458"
id="g48">
<rect
x="44.979"
y="32.417"
width="22.49"
height="6.6146"
fill="none"
stroke-dasharray="0.52916663, 0.52916663"
id="rect36" />
<rect
x="67.469"
y="32.417"
width="22.49"
height="6.6146"
fill="#d7d7f4"
id="rect38" />
<rect
x="89.958"
y="32.417"
width="22.49"
height="6.6146"
fill="#d7d7f4"
id="rect40" />
<rect
x="112.45"
y="32.417"
width="22.49"
height="6.6146"
fill="none"
stroke-dasharray="0.52916663, 0.52916663"
id="rect42" />
<rect
x="134.94"
y="32.417"
width="22.49"
height="6.6146"
fill="none"
stroke-dasharray="0.52916663, 0.52916663"
id="rect44" />
<rect
x="157.43"
y="32.417"
width="22.49"
height="6.6146"
fill="#d7d7f4"
id="rect46" />
</g>
<rect
x="44.979"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect50" />
</g>
<text
x="56.412949"
y="51.598957"
fill="#000000"
font-family="sans-serif"
font-size="10.583px"
letter-spacing="0px"
stroke-width="0.26458"
word-spacing="0px"
style="line-height:1.25"
xml:space="preserve"
id="text56"><tspan
x="56.412949"
y="51.598957"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan54">26f9a7...</tspan></text>
<rect
x="67.469"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke="#000"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect58" />
<text
x="78.902527"
y="51.598961"
fill="#000000"
font-family="sans-serif"
font-size="10.583px"
letter-spacing="0px"
stroke-width="0.26458"
word-spacing="0px"
style="line-height:1.25"
xml:space="preserve"
id="text62"><tspan
x="78.902527"
y="51.598961"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan60">b44ab3...</tspan></text>
<rect
x="89.958"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke="#000"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect64" />
<text
x="101.39211"
y="51.598961"
fill="#000000"
font-family="sans-serif"
font-size="10.583px"
letter-spacing="0px"
stroke-width="0.26458"
word-spacing="0px"
style="line-height:1.25"
xml:space="preserve"
id="text68"><tspan
x="101.39211"
y="51.598961"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan66">ee5593...</tspan></text>
<rect
x="112.45"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke="#000"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect70" />
<text
x="123.88169"
y="51.598961"
fill="#000000"
font-family="sans-serif"
font-size="10.583px"
letter-spacing="0px"
stroke-width="0.26458"
word-spacing="0px"
style="line-height:1.25"
xml:space="preserve"
id="text74"><tspan
x="123.88169"
y="51.598961"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan72">7a3bfe...</tspan></text>
<rect
x="134.94"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke="#000"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect76" />
<text
x="146.37128"
y="51.598957"
fill="#000000"
font-family="sans-serif"
font-size="10.583px"
letter-spacing="0px"
stroke-width="0.26458"
word-spacing="0px"
style="line-height:1.25"
xml:space="preserve"
id="text80"><tspan
x="146.37128"
y="51.598957"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan78">8f4e15...</tspan></text>
<rect
x="157.43"
y="46.969"
width="22.49"
height="6.6146"
fill="#f4d7d7"
stroke="#000"
stroke-dasharray="0.52999997, 0.26499999"
stroke-width=".265"
id="rect82" />
<g
font-family="sans-serif"
letter-spacing="0px"
stroke-width=".26458"
word-spacing="0px"
id="g98">
<text
x="168.86086"
y="51.598961"
font-size="10.583px"
style="line-height:1.25"
xml:space="preserve"
id="text86"><tspan
x="168.86086"
y="51.598961"
font-family="sans-serif"
font-size="3.5278px"
stroke-width="0.26458"
text-align="center"
text-anchor="middle"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan84">40c285...</tspan></text>
<text
x="6.6430736"
y="51.680019"
font-size="3.5278px"
style="line-height:1.25;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
xml:space="preserve"
id="text90"><tspan
x="6.6430736"
y="51.680019"
stroke-width="0.26458"
id="tspan88">read-only bdev</tspan></text>
<text
x="6.6296382"
y="12.539818"
font-size="3.5278px"
style="line-height:1.25;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
xml:space="preserve"
id="text96"><tspan
sodipodi:role="line"
id="tspan436"
x="6.6296382"
y="12.539818">esnap clone</tspan><tspan
sodipodi:role="line"
x="6.6296382"
y="16.949568"
id="tspan440">Volume</tspan><tspan
sodipodi:role="line"
id="tspan438"
x="6.6296382"
y="21.359318" /></text>
</g>
<g
stroke="#000"
id="g118">
<path
d="m6.6146 24.479 173.3 1e-6"
fill="none"
stroke-dasharray="1.59, 1.59"
stroke-width=".265"
id="path100" />
<g
fill="#f4d7d7"
stroke-dasharray="0.52916663, 0.26458332"
stroke-width=".26458"
id="g108">
<rect
x="44.979"
y="9.9271"
width="22.49"
height="6.6146"
id="rect102" />
<rect
x="112.45"
y="9.9271"
width="22.49"
height="6.6146"
id="rect104" />
<rect
x="134.94"
y="9.9271"
width="22.49"
height="6.6146"
id="rect106" />
</g>
<g
fill="#d7d7f4"
stroke-width=".26458"
id="g116">
<rect
x="67.469"
y="9.9271"
width="22.49"
height="6.6146"
id="rect110" />
<rect
x="89.958"
y="9.9271"
width="22.49"
height="6.6146"
id="rect112" />
<rect
x="157.43"
y="9.9271"
width="22.49"
height="6.6146"
id="rect114" />
</g>
</g>
<text
x="6.614583"
y="37.708332"
fill="#000000"
font-family="sans-serif"
font-size="3.5278px"
letter-spacing="0px"
stroke-width=".26458"
word-spacing="0px"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25"
xml:space="preserve"
id="text122"><tspan
x="6.614583"
y="37.708332"
stroke-width=".26458"
id="tspan120">active clusters</tspan></text>
<rect
x="37.042"
y="7.2812"
width="145.52"
height="11.906"
ry="1.3229"
fill="none"
stroke="#999"
stroke-width=".5"
id="rect124" />
<rect
x="37.042"
y="29.771"
width="145.52"
height="26.458"
ry="1.3229"
fill="none"
stroke="#999"
stroke-width=".5"
id="rect126" />
<g
fill="#00f"
stroke="#00f"
id="g144">
<g
stroke-width=".26458"
id="g140">
<path
d="m78.052 16.542v15.875"
marker-end="url(#marker1960)"
id="path128" />
<path
d="m55.562 16.542v30.427"
marker-end="url(#marker2036)"
id="path130" />
<path
d="m100.54 16.542v15.875"
marker-end="url(#marker1890)"
id="path132" />
<path
d="m169.33 16.542v15.875"
marker-end="url(#Arrow1Mend)"
id="path134" />
<path
d="m124.35 16.542v30.427"
marker-end="url(#marker1826)"
id="path136" />
<path
d="m146.84 16.542v30.427"
marker-end="url(#marker1816)"
id="path138" />
</g>
<path
d="m132.29 61.521 10.583 1e-5"
marker-end="url(#marker1826-2-4-7-1-7)"
stroke-width=".265"
id="path142" />
</g>
<path
d="m132.29 66.813h10.583"
fill="#f00"
marker-end="url(#marker11771-4-9)"
stroke="#ff2a2a"
stroke-width=".265"
id="path146" />
<g
stroke-width=".26458"
id="g162">
<text
x="145.52083"
y="62.843975"
fill="#000000"
font-family="sans-serif"
font-size="3.5278px"
letter-spacing="0px"
word-spacing="0px"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25"
xml:space="preserve"
id="text150"><tspan
x="145.52083"
y="62.843975"
font-family="sans-serif"
font-size="2.8222px"
stroke-width=".26458"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"
id="tspan148">read</tspan></text>
<text
x="145.52083"
y="68.135651"
fill="#000000"
font-family="sans-serif"
font-size="3.5278px"
letter-spacing="0px"
word-spacing="0px"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25"
xml:space="preserve"
id="text154"><tspan
x="145.52083"
y="68.135651"
font-family="sans-serif"
font-size="2.8222px"
stroke-width=".26458"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"
id="tspan152">allocate and copy cluster</tspan></text>
<rect
x="132.29"
y="70.781"
width="10.583"
height="2.6458"
fill="none"
stroke="#000"
stroke-dasharray="0.52916664, 0.52916664"
id="rect156" />
<text
x="145.52083"
y="73.427307"
fill="#000000"
font-family="sans-serif"
font-size="3.5278px"
letter-spacing="0px"
word-spacing="0px"
style="line-height:1.25;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
xml:space="preserve"
id="text160"><tspan
x="145.52083"
y="73.427307"
font-family="sans-serif"
font-size="2.8222px"
stroke-width="0.26458"
style="font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal"
id="tspan158">external snapshot cluster</tspan></text>
</g>
<rect
x="132.29"
y="76.073"
width="10.583"
height="2.6458"
fill="none"
stroke="#000"
stroke-width=".265"
id="rect164" />
<text
x="145.52083"
y="78.718971"
fill="#000000"
font-family="sans-serif"
font-size="3.5278px"
letter-spacing="0px"
stroke-width=".26458"
word-spacing="0px"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;line-height:1.25"
xml:space="preserve"
id="text168"><tspan
x="145.52083"
y="78.718971"
font-family="sans-serif"
font-size="2.8222px"
stroke-width=".26458"
style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"
id="tspan166">allocated cluster</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -9636,6 +9636,55 @@ Example response:
} }
~~~ ~~~
### bdev_lvol_clone_bdev {#rpc_bdev_lvol_clone_bdev}
Create a logical volume based on an external snapshot bdev. The external snapshot bdev
is a bdev that will not be written to by any consumer and must not be an lvol in the
lvstore as the clone.
Regardless of whether the bdev is specified by name or UUID, the bdev UUID will be stored
in the logical volume's metadata for use while the lvolstore is loading. For this reason,
it is important that the bdev chosen has a static UUID.
#### Parameters
Name | Optional | Type | Description
----------------------- | -------- | ----------- | -----------
bdev | Required | string | Name or UUID for bdev that acts as the external snapshot
lvs_name | Required | string | logical volume store name
clone_name | Required | string | Name for the logical volume to create
#### Response
UUID of the created logical volume clone is returned.
#### Example
Example request:
~~~json
{
"jsonrpc": "2.0",
"method": "bdev_lvol_clone_bdev",
"id": 1,
"params": {
"bdev_uuid": "e4b40d8b-f623-416d-8234-baf5a4c83cbd",
"lvs_name": "lvs1",
"clone_name": "clone2"
}
}
~~~
Example response:
~~~json
{
"jsonrpc": "2.0",
"id": 1,
"result": "336f662b-08e5-4006-8e06-e2023f7f9886"
}
~~~
### bdev_lvol_rename {#rpc_bdev_lvol_rename} ### bdev_lvol_rename {#rpc_bdev_lvol_rename}
Rename a logical volume. New name will rename only the alias of the logical volume. Rename a logical volume. New name will rename only the alias of the logical volume.

View File

@ -74,6 +74,18 @@ A snapshot can be removed only if there is a single clone on top of it. The rela
The cluster map of clone and snapshot will be merged and entries for unallocated clusters in the clone will be updated with The cluster map of clone and snapshot will be merged and entries for unallocated clusters in the clone will be updated with
addresses from the snapshot cluster map. The entire operation modifies metadata only - no data is copied during this process. addresses from the snapshot cluster map. The entire operation modifies metadata only - no data is copied during this process.
### External Snapshots
With the external snapshots feature, clones can be made of any bdev. These clones are commonly called *esnap clones*.
Esnap clones work very similarly to thin provisioning. Rather than the back device being an zeroes device, the external snapshot
bdev is used as the back device.
![Clone of External Snapshot](lvol_esnap_clone.svg)
A bdev that is used as an external snapshot cannot be opened for writing by anything else so long as an esnap clone exists.
A bdev may have multiple esnap clones and esnap clones can themselves be snapshotted and cloned.
### Inflation {#lvol_inflation} ### Inflation {#lvol_inflation}
Blobs can be inflated to copy data from backing devices (e.g. snapshots) and allocate all remaining clusters. As a result of this Blobs can be inflated to copy data from backing devices (e.g. snapshots) and allocate all remaining clusters. As a result of this
@ -155,6 +167,10 @@ bdev_lvol_clone [-h] snapshot_name clone_name
Create a clone with clone_name of a given lvol snapshot. Create a clone with clone_name of a given lvol snapshot.
optional arguments: optional arguments:
-h, --help show help -h, --help show help
bdev_lvol_clone_bdev [-h] bdev_name_or_uuid lvs_name clone_name
Create a clone with clone_name of a bdev. The bdev must not be an lvol in the lvs_name lvstore.
optional arguments:
-h, --help show help
bdev_lvol_rename [-h] old_name new_name bdev_lvol_rename [-h] old_name new_name
Change lvol bdev name Change lvol bdev name
optional arguments: optional arguments:

View File

@ -1150,7 +1150,7 @@ ignore_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, voi
} }
void void
vbdev_lvol_create_bdev_clone(const char *esnap_uuid, vbdev_lvol_create_bdev_clone(const char *esnap_name,
struct spdk_lvol_store *lvs, const char *clone_name, struct spdk_lvol_store *lvs, const char *clone_name,
spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg) spdk_lvol_op_with_handle_complete cb_fn, void *cb_arg)
{ {
@ -1158,7 +1158,6 @@ vbdev_lvol_create_bdev_clone(const char *esnap_uuid,
struct spdk_bdev_desc *desc; struct spdk_bdev_desc *desc;
struct spdk_bdev *bdev; struct spdk_bdev *bdev;
char bdev_uuid[SPDK_UUID_STRING_LEN]; char bdev_uuid[SPDK_UUID_STRING_LEN];
struct spdk_uuid uuid;
uint64_t sz; uint64_t sz;
int rc; int rc;
@ -1168,16 +1167,9 @@ vbdev_lvol_create_bdev_clone(const char *esnap_uuid,
return; return;
} }
rc = spdk_uuid_parse(&uuid, esnap_uuid); rc = spdk_bdev_open_ext(esnap_name, false, ignore_bdev_event_cb, NULL, &desc);
if (rc != 0) { if (rc != 0) {
SPDK_ERRLOG("Invalid UUID '%s'\n", esnap_uuid); SPDK_ERRLOG("bdev '%s' could not be opened: error %d\n", esnap_name, rc);
cb_fn(cb_arg, NULL, -EINVAL);
return;
}
rc = spdk_bdev_open_ext(esnap_uuid, false, ignore_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("bdev '%s' could not be opened: error %d\n", esnap_uuid, rc);
cb_fn(cb_arg, NULL, rc); cb_fn(cb_arg, NULL, rc);
return; return;
} }
@ -1186,20 +1178,12 @@ vbdev_lvol_create_bdev_clone(const char *esnap_uuid,
rc = spdk_uuid_fmt_lower(bdev_uuid, sizeof(bdev_uuid), spdk_bdev_get_uuid(bdev)); rc = spdk_uuid_fmt_lower(bdev_uuid, sizeof(bdev_uuid), spdk_bdev_get_uuid(bdev));
if (rc != 0) { if (rc != 0) {
spdk_bdev_close(desc); spdk_bdev_close(desc);
SPDK_ERRLOG("bdev %s: unable to parse UUID\n", esnap_uuid); SPDK_ERRLOG("bdev %s: unable to parse UUID\n", esnap_name);
assert(false); assert(false);
cb_fn(cb_arg, NULL, -ENODEV); cb_fn(cb_arg, NULL, -ENODEV);
return; return;
} }
/* Verify the bdev name or alias isn't a UUID that is different from the bdev's UUID. */
if (spdk_uuid_compare(&uuid, spdk_bdev_get_uuid(bdev)) != 0) {
spdk_bdev_close(desc);
SPDK_ERRLOG("bdev with name or alias %s has UUID %s\n", esnap_uuid, bdev_uuid);
cb_fn(cb_arg, NULL, -EINVAL);
return;
}
req = calloc(1, sizeof(*req)); req = calloc(1, sizeof(*req));
if (req == NULL) { if (req == NULL) {
spdk_bdev_close(desc); spdk_bdev_close(desc);

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause /* SPDX-License-Identifier: BSD-3-Clause
* Copyright (C) 2017 Intel Corporation. * Copyright (C) 2017 Intel Corporation.
* All rights reserved. * All rights reserved.
* Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*/ */
#include "spdk/rpc.h" #include "spdk/rpc.h"
@ -540,6 +541,89 @@ cleanup:
SPDK_RPC_REGISTER("bdev_lvol_clone", rpc_bdev_lvol_clone, SPDK_RPC_RUNTIME) SPDK_RPC_REGISTER("bdev_lvol_clone", rpc_bdev_lvol_clone, SPDK_RPC_RUNTIME)
struct rpc_bdev_lvol_clone_bdev {
/* name or UUID. Whichever is used, the UUID will be stored in the lvol's metadata. */
char *bdev_name;
char *lvs_name;
char *clone_name;
};
static void
free_rpc_bdev_lvol_clone_bdev(struct rpc_bdev_lvol_clone_bdev *req)
{
free(req->bdev_name);
free(req->lvs_name);
free(req->clone_name);
}
static const struct spdk_json_object_decoder rpc_bdev_lvol_clone_bdev_decoders[] = {
{
"bdev", offsetof(struct rpc_bdev_lvol_clone_bdev, bdev_name),
spdk_json_decode_string, false
},
{
"lvs_name", offsetof(struct rpc_bdev_lvol_clone_bdev, lvs_name),
spdk_json_decode_string, false
},
{
"clone_name", offsetof(struct rpc_bdev_lvol_clone_bdev, clone_name),
spdk_json_decode_string, false
},
};
static void
rpc_bdev_lvol_clone_bdev(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params)
{
struct rpc_bdev_lvol_clone_bdev req = {};
struct spdk_bdev *bdev;
struct spdk_lvol_store *lvs = NULL;
struct spdk_lvol *lvol;
int rc;
SPDK_INFOLOG(lvol_rpc, "Cloning bdev\n");
if (spdk_json_decode_object(params, rpc_bdev_lvol_clone_bdev_decoders,
SPDK_COUNTOF(rpc_bdev_lvol_clone_bdev_decoders), &req)) {
SPDK_INFOLOG(lvol_rpc, "spdk_json_decode_object failed\n");
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR,
"spdk_json_decode_object failed");
goto cleanup;
}
rc = vbdev_get_lvol_store_by_uuid_xor_name(NULL, req.lvs_name, &lvs);
if (rc != 0) {
SPDK_INFOLOG(lvol_rpc, "lvs_name '%s' not found\n", req.lvs_name);
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"lvs does not exist");
goto cleanup;
}
bdev = spdk_bdev_get_by_name(req.bdev_name);
if (bdev == NULL) {
SPDK_INFOLOG(lvol_rpc, "bdev '%s' does not exist\n", req.bdev_name);
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"bdev does not exist");
goto cleanup;
}
lvol = vbdev_lvol_get_from_bdev(bdev);
if (lvol != NULL && lvol->lvol_store == lvs) {
SPDK_INFOLOG(lvol_rpc, "bdev '%s' is an lvol in lvstore '%s\n", req.bdev_name,
req.lvs_name);
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
"bdev is an lvol in same lvs as clone; "
"use bdev_lvol_clone instead");
goto cleanup;
}
vbdev_lvol_create_bdev_clone(req.bdev_name, lvs, req.clone_name,
rpc_bdev_lvol_clone_cb, request);
cleanup:
free_rpc_bdev_lvol_clone_bdev(&req);
}
SPDK_RPC_REGISTER("bdev_lvol_clone_bdev", rpc_bdev_lvol_clone_bdev, SPDK_RPC_RUNTIME)
struct rpc_bdev_lvol_rename { struct rpc_bdev_lvol_rename {
char *old_name; char *old_name;
char *new_name; char *new_name;

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Copyright (C) 2017 Intel Corporation. # Copyright (C) 2017 Intel Corporation.
# All rights reserved. # All rights reserved.
# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
def bdev_lvol_create_lvstore(client, bdev_name, lvs_name, cluster_sz=None, def bdev_lvol_create_lvstore(client, bdev_name, lvs_name, cluster_sz=None,
@ -122,6 +123,30 @@ def bdev_lvol_clone(client, snapshot_name, clone_name):
return client.call('bdev_lvol_clone', params) return client.call('bdev_lvol_clone', params)
def bdev_lvol_clone_bdev(client, bdev, lvs_name, clone_name):
"""Create a logical volume based on a snapshot.
Regardless of whether the bdev is specified by name or UUID, the bdev UUID
will be stored in the logical volume's metadata for use while the lvolstore
is loading. For this reason, it is important that the bdev chosen has a
static UUID.
Args:
bdev: bdev to clone; must not be an lvol in same lvstore as clone
lvs_name: name of logical volume store to use
clone_name: name of logical volume to create
Returns:
Name of created logical volume clone.
"""
params = {
'bdev': bdev,
'lvs_name': lvs_name,
'clone_name': clone_name
}
return client.call('bdev_lvol_clone_bdev', params)
def bdev_lvol_rename(client, old_name, new_name): def bdev_lvol_rename(client, old_name, new_name):
"""Rename a logical volume. """Rename a logical volume.

View File

@ -3,6 +3,7 @@
# Copyright (C) 2016 Intel Corporation # Copyright (C) 2016 Intel Corporation
# All rights reserved. # All rights reserved.
# Copyright (c) 2022 Dell Inc, or its subsidiaries. # Copyright (c) 2022 Dell Inc, or its subsidiaries.
# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# #
import logging import logging
@ -1966,6 +1967,19 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse
p.add_argument('clone_name', help='lvol clone name') p.add_argument('clone_name', help='lvol clone name')
p.set_defaults(func=bdev_lvol_clone) p.set_defaults(func=bdev_lvol_clone)
def bdev_lvol_clone_bdev(args):
print_json(rpc.lvol.bdev_lvol_clone_bdev(args.client,
bdev=args.bdev,
lvs_name=args.lvs_name,
clone_name=args.clone_name))
p = subparsers.add_parser('bdev_lvol_clone_bdev',
help='Create a clone of a non-lvol bdev')
p.add_argument('bdev', help='bdev to clone')
p.add_argument('lvs_name', help='logical volume store name')
p.add_argument('clone_name', help='lvol clone name')
p.set_defaults(func=bdev_lvol_clone_bdev)
def bdev_lvol_rename(args): def bdev_lvol_rename(args):
rpc.lvol.bdev_lvol_rename(args.client, rpc.lvol.bdev_lvol_rename(args.client,
old_name=args.old_name, old_name=args.old_name,

View File

@ -609,6 +609,12 @@ function rpc_cmd_simple_data_json() {
"num_blocks" "num_blocks"
"uuid" "uuid"
"product_name" "product_name"
"supported_io_types.read"
"supported_io_types.write"
"driver_specific.lvol.clone"
"driver_specific.lvol.base_snapshot"
"driver_specific.lvol.esnap_clone"
"driver_specific.lvol.external_snapshot_name"
) )
[[ -v $elems ]] || return 1 [[ -v $elems ]] || return 1

232
test/lvol/external_snapshot.sh Executable file
View File

@ -0,0 +1,232 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
testdir=$(readlink -f "$(dirname "$0")")
rootdir=$(readlink -f "$testdir/../..")
source "$rootdir/test/common/autotest_common.sh"
source "$rootdir/test/lvol/common.sh"
source "$rootdir/test/bdev/nbd_common.sh"
set -u
g_nbd_dev=INVALID
g_cluster_size=INVALID
g_block_size=INVALID
function test_esnap_reload() {
local bs_dev esnap_dev
local block_size=512
local esnap_size_mb=1
local lvs_cluster_size=$((16 * 1024))
local lvs_uuid esnap_uuid eclone_uuid snap_uuid clone_uuid uuid
local aio_bdev=test_esnap_reload_aio0
# Create the lvstore on an aio device. Can't use malloc because we need to remove
# the device and re-add it to trigger an lvstore unload and then load.
rm -f $testdir/aio_bdev_0
truncate -s "${AIO_SIZE_MB}M" $testdir/aio_bdev_0
bs_dev=$(rpc_cmd bdev_aio_create "$testdir/aio_bdev_0" "$aio_bdev" "$block_size")
lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore -c "$lvs_cluster_size" "$bs_dev" lvs_test)
# Create a bdev that will be the external snapshot
esnap_uuid=e4b40d8b-f623-416d-8234-baf5a4c83cbd
esnap_dev=$(rpc_cmd bdev_malloc_create -u "$esnap_uuid" "$esnap_size_mb" "$block_size")
eclone_uuid=$(rpc_cmd bdev_lvol_clone_bdev "$esnap_uuid" lvs_test "eclone1")
# Unload the lvstore
rpc_cmd bdev_aio_delete "$aio_bdev"
NOT rpc_cmd bdev_lvol_get_lvstores -l lvs_test
# Load the lvstore, expect to see eclone1 again
bs_dev=$(rpc_cmd bdev_aio_create "$testdir/aio_bdev_0" "$aio_bdev" "$block_size")
lvs_uuid=$(rpc_cmd bdev_lvol_get_lvstores -l lvs_test)
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/eclone1 | jq -r '.[].name')
[[ "$uuid" == "$eclone_uuid" ]]
# Create a snapshot of the eclone, reload, and verify all is there.
snap_uuid=$(rpc_cmd bdev_lvol_snapshot "$eclone_uuid" snap1)
rpc_cmd bdev_aio_delete "$aio_bdev"
NOT rpc_cmd bdev_lvol_get_lvstores -l lvs_test
bs_dev=$(rpc_cmd bdev_aio_create "$testdir/aio_bdev_0" "$aio_bdev" "$block_size")
lvs_uuid=$(rpc_cmd bdev_lvol_get_lvstores -l lvs_test)
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/eclone1 | jq -r '.[].name')
[[ "$uuid" == "$eclone_uuid" ]]
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/snap1 | jq -r '.[].name')
[[ "$uuid" == "$snap_uuid" ]]
# Create a clone of the snapshot, reload, and verify all is there.
clone_uuid=$(rpc_cmd bdev_lvol_clone "$snap_uuid" clone1)
rpc_cmd bdev_aio_delete "$aio_bdev"
NOT rpc_cmd bdev_lvol_get_lvstores -l lvs_test
bs_dev=$(rpc_cmd bdev_aio_create "$testdir/aio_bdev_0" "$aio_bdev" "$block_size")
lvs_uuid=$(rpc_cmd bdev_lvol_get_lvstores -l lvs_test)
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/eclone1 | jq -r '.[].name')
[[ "$uuid" == "$eclone_uuid" ]]
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/snap1 | jq -r '.[].name')
[[ "$uuid" == "$snap_uuid" ]]
uuid=$(rpc_cmd bdev_get_bdevs -b lvs_test/clone1 | jq -r '.[].name')
[[ "$uuid" == "$clone_uuid" ]]
rpc_cmd bdev_lvol_delete "$clone_uuid"
rpc_cmd bdev_lvol_delete "$snap_uuid"
rpc_cmd bdev_lvol_delete "$eclone_uuid"
rpc_cmd bdev_aio_delete "$aio_bdev"
rpc_cmd bdev_malloc_delete "$esnap_dev"
}
function log_jq_out() {
local key
xtrace_disable
while read -r key; do
printf '%50s = %s\n' "$key" "${jq_out[$key]}"
done < <(printf '%s\n' "${!jq_out[@]}" | sort)
xtrace_restore
}
function verify_clone() {
local bdev=$1
local parent=$2
rpc_cmd_simple_data_json bdev bdev_get_bdevs -b "$bdev"
log_jq_out
[[ "${jq_out["supported_io_types.read"]}" == true ]]
[[ "${jq_out["supported_io_types.write"]}" == true ]]
[[ "${jq_out["driver_specific.lvol.clone"]}" == true ]]
[[ "${jq_out["driver_specific.lvol.base_snapshot"]}" == "$parent" ]]
[[ "${jq_out["driver_specific.lvol.esnap_clone"]}" == false ]]
[[ "${jq_out["driver_specific.lvol.external_snapshot_name"]}" == null ]]
}
function verify_esnap_clone() {
local bdev=$1
local parent=$2
local writable=${3:-true}
rpc_cmd_simple_data_json bdev bdev_get_bdevs -b "$bdev"
log_jq_out
[[ "${jq_out["supported_io_types.read"]}" == true ]]
[[ "${jq_out["supported_io_types.write"]}" == "$writable" ]]
[[ "${jq_out["driver_specific.lvol.esnap_clone"]}" == true ]]
[[ "${jq_out["driver_specific.lvol.external_snapshot_name"]}" == "$parent" ]]
}
function test_esnap_clones() {
local bs_dev esnap_dev
local block_size=512
local lvs_size_mb=100
local esnap_size_mb=1
local lvs_cluster_size=$((16 * 1024))
local lvs_uuid esnap_uuid
local vol1_uuid vol2_uuid vol3_uuid vol3_uuid vol4_uuid vol5_uuid
# Create the lvstore on a malloc device.
bs_dev=$(rpc_cmd bdev_malloc_create $lvs_size_mb $block_size)
lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore -c "$lvs_cluster_size" "$bs_dev" lvs_test)
# Create a bdev that will be the external snapshot
# State:
# esnap1
esnap_uuid=2abddd12-c08d-40ad-bccf-ab131586ee4c
esnap_dev=$(rpc_cmd bdev_malloc_create -b esnap1 -u "$esnap_uuid" "$esnap_size_mb" \
"$block_size")
# Create an esnap clone: vol1
# New state:
# esnap1 <-- vol1(rw)
vol1_uuid=$(rpc_cmd bdev_lvol_clone_bdev "$esnap_uuid" lvs_test vol1)
verify_esnap_clone "$vol1_uuid" "$esnap_uuid"
# Create a snapshot of the esnap clone: vol2
# New state:
# esnap1 <-- vol2(ro) <-- vol1(rw)
vol2_uuid=$(rpc_cmd bdev_lvol_snapshot "$vol1_uuid" vol2)
verify_esnap_clone "$vol2_uuid" "$esnap_uuid" false
verify_clone "$vol1_uuid" vol2
# Delete vol2.
# New state:
# esnap1 <-- vol1(rw)
rpc_cmd bdev_lvol_delete "$vol2_uuid"
NOT rpc_cmd bdev_get_bdevs -b "$vol2_uuid"
verify_esnap_clone "$vol1_uuid" "$esnap_uuid"
vol2_uuid=
# Snapshot vol1: vol3
# New state:
# ensap1 <-- vol3(ro) <-- vol1(rw)
vol3_uuid=$(rpc_cmd bdev_lvol_snapshot "$vol1_uuid" vol3)
verify_esnap_clone "$vol3_uuid" "$esnap_uuid" false
verify_clone "$vol1_uuid" vol3
# Delete vol1
# New state:
# esnap1 <-- vol3(ro)
rpc_cmd bdev_lvol_delete $vol1_uuid
NOT rpc_cmd bdev_get_bdevs -b $vol1_uuid
verify_esnap_clone "$vol3_uuid" "$esnap_uuid" false
vol1_uuid=
# Create clone of vol3: vol4
# Verify vol3 is still a read-only esnap clone and vol4 is a normal clone.
# New state:
# ensap1 <-- vol3(ro) <-- vol4(rw)
vol4_uuid=$(rpc_cmd bdev_lvol_clone "$vol3_uuid" vol4)
rpc_cmd bdev_get_bdevs -b "$vol4_uuid"
verify_esnap_clone "$vol3_uuid" "$esnap_uuid" false
verify_clone "$vol4_uuid" vol3
# Create clone of vol3 (vol5).
# New state:
# ensap1 <-- vol3(ro) <-- vol4(rw)
# `<-- vol5(rw)
vol5_uuid=$(rpc_cmd bdev_lvol_clone "$vol3_uuid" vol5)
verify_esnap_clone "$vol3_uuid" "$esnap_uuid" false
verify_clone "$vol4_uuid" vol3
verify_clone "$vol5_uuid" vol3
# Cannot delete vol3 because it has multiple clones
NOT rpc_cmd bdev_lvol_delete "$vol3_uuid"
# Delete vol4
# New state:
# ensap1 <-- vol3(ro) <-- vol5(rw)
rpc_cmd bdev_lvol_delete "$vol4_uuid"
NOT rpc_cmd bdev_get_bdevs -b "$vol4_uuid"
verify_esnap_clone "$vol3_uuid" "$esnap_uuid" false
verify_clone "$vol5_uuid" vol3
# Delete vol3.
# New state:
# ensap1 <-- vol5(rw)
rpc_cmd bdev_lvol_delete "$vol3_uuid"
NOT rpc_cmd bdev_get_bdevs -b "$vol3_uuid"
verify_esnap_clone "$vol5_uuid" "$esnap_uuid"
# Delete vol5.
# New state:
# esnap1
rpc_cmd bdev_lvol_delete "$vol5_uuid"
NOT rpc_cmd bdev_get_bdevs -b "$vol5_uuid"
rpc_cmd bdev_malloc_delete "$bs_dev"
rpc_cmd bdev_malloc_delete "$esnap_dev"
}
$SPDK_BIN_DIR/spdk_tgt &
spdk_pid=$!
trap 'killprocess "$spdk_pid"; rm -f "$testdir/aio_bdev_0"; exit 1' SIGINT SIGTERM SIGPIPE EXIT
waitforlisten $spdk_pid
modprobe nbd
run_test "test_esnap_reload" test_esnap_reload
run_test "test_esnap_clones" test_esnap_clones
trap - SIGINT SIGTERM SIGPIPE EXIT
killprocess $spdk_pid
rm -f "$testdir/aio_bdev_0"

View File

@ -19,6 +19,7 @@ run_test "lvol_snapshot_clone" $rootdir/test/lvol/snapshot_clone.sh
run_test "lvol_rename" $rootdir/test/lvol/rename.sh run_test "lvol_rename" $rootdir/test/lvol/rename.sh
run_test "lvol_provisioning" $rootdir/test/lvol/thin_provisioning.sh run_test "lvol_provisioning" $rootdir/test/lvol/thin_provisioning.sh
run_test "lvol_esnap" $rootdir/test/lvol/esnap/esnap run_test "lvol_esnap" $rootdir/test/lvol/esnap/esnap
run_test "lvol_external_snapshot" $rootdir/test/lvol/external_snapshot.sh
timing_exit basic timing_exit basic
timing_exit lvol timing_exit lvol

View File

@ -1826,7 +1826,7 @@ ut_lvol_esnap_clone_bad_args(void)
struct spdk_bdev bdev = { 0 }; struct spdk_bdev bdev = { 0 };
struct spdk_lvol_store *lvs; struct spdk_lvol_store *lvs;
const char *esnap_uuid = "255f4236-9427-42d0-a9d1-aa17f37dd8db"; const char *esnap_uuid = "255f4236-9427-42d0-a9d1-aa17f37dd8db";
const char *name_uuid = "5c164b0a-93af-434f-ac35-51af59791f3b"; const char *esnap_name = "esnap1";
int rc; int rc;
/* Lvol store is successfully created */ /* Lvol store is successfully created */
@ -1840,7 +1840,7 @@ ut_lvol_esnap_clone_bad_args(void)
rc = spdk_uuid_parse(&bdev.uuid, esnap_uuid); rc = spdk_uuid_parse(&bdev.uuid, esnap_uuid);
CU_ASSERT(rc == 0); CU_ASSERT(rc == 0);
bdev.name = strdup(name_uuid); bdev.name = strdup(esnap_name);
SPDK_CU_ASSERT_FATAL(bdev.name != NULL); SPDK_CU_ASSERT_FATAL(bdev.name != NULL);
bdev.blocklen = 512; bdev.blocklen = 512;
bdev.blockcnt = 8192; bdev.blockcnt = 8192;
@ -1852,23 +1852,23 @@ ut_lvol_esnap_clone_bad_args(void)
vbdev_lvol_create_bdev_clone(esnap_uuid, NULL, "clone1", vbdev_lvol_create_complete, NULL); vbdev_lvol_create_bdev_clone(esnap_uuid, NULL, "clone1", vbdev_lvol_create_complete, NULL);
CU_ASSERT(g_lvolerrno == -EINVAL); CU_ASSERT(g_lvolerrno == -EINVAL);
/* Error when a uuid-like name is provided and that matches the bdev name but not uuid */
g_lvolerrno = 0xbad;
vbdev_lvol_create_bdev_clone(name_uuid, lvs, "clone1", vbdev_lvol_create_complete, NULL);
CU_ASSERT(g_lvolerrno == -EINVAL);
/* Error when the bdev does not exist */ /* Error when the bdev does not exist */
g_base_bdev = NULL; g_base_bdev = NULL;
g_lvolerrno = 0xbad; g_lvolerrno = 0xbad;
vbdev_lvol_create_bdev_clone(esnap_uuid, lvs, "clone1", vbdev_lvol_create_complete, NULL); vbdev_lvol_create_bdev_clone(esnap_uuid, lvs, "clone1", vbdev_lvol_create_complete, NULL);
CU_ASSERT(g_lvolerrno == -ENODEV); CU_ASSERT(g_lvolerrno == -ENODEV);
/* Success when the stars all align. */ /* Success when creating by bdev UUID */
g_base_bdev = &bdev; g_base_bdev = &bdev;
g_lvolerrno = 0xbad; g_lvolerrno = 0xbad;
vbdev_lvol_create_bdev_clone(esnap_uuid, lvs, "clone1", vbdev_lvol_create_complete, NULL); vbdev_lvol_create_bdev_clone(esnap_uuid, lvs, "clone1", vbdev_lvol_create_complete, NULL);
CU_ASSERT(g_lvolerrno == 0); CU_ASSERT(g_lvolerrno == 0);
/* Success when creating by bdev name */
g_lvolerrno = 0xbad;
vbdev_lvol_create_bdev_clone(esnap_name, lvs, "clone2", vbdev_lvol_create_complete, NULL);
CU_ASSERT(g_lvolerrno == 0);
g_lvol_store = lvs; g_lvol_store = lvs;
vbdev_lvs_destruct(g_lvol_store, lvol_store_op_complete, NULL); vbdev_lvs_destruct(g_lvol_store, lvol_store_op_complete, NULL);
CU_ASSERT(g_lvserrno == 0); CU_ASSERT(g_lvserrno == 0);