librbd: add rbd_clone4() API to take parent snapshot by ID

Allow cloning from non-user snapshots -- namely snapshots in group
and mirror namespaces.  The motivation is to provide a building block
for cloning new groups from group snapshots ("rbd group snap create").
Otherwise, group snapshots as they are today can be used only for
rolling back the group as a whole, which is very limiting.

While at it, there doesn't seem to be anything wrong with making it
possible to clone from mirror snapshots as well.

Snapshots in a trash namespace can't be cloned from since they are
considered to be deleted.

Cloning from non-user snapshots is limited to clone v2 just because
protecting/unprotecting is limited to snapshots in a user namespace.
This happens to simplify some invariants.

Fixes: https://tracker.ceph.com/issues/64662
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
Ilya Dryomov 2024-05-24 12:06:09 +02:00
parent 533943d08f
commit d7fd66ec99
11 changed files with 288 additions and 27 deletions

View File

@ -479,6 +479,9 @@ CEPH_RBD_API int rbd_clone2(rados_ioctx_t p_ioctx, const char *p_name,
CEPH_RBD_API int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
const char *p_snapname, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts);
CEPH_RBD_API int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
uint64_t p_snap_id, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts);
CEPH_RBD_API int rbd_remove(rados_ioctx_t io, const char *name);
CEPH_RBD_API int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
librbd_progress_fn_t cb,

View File

@ -294,6 +294,8 @@ public:
int *c_order, uint64_t stripe_unit, int stripe_count);
int clone3(IoCtx& p_ioctx, const char *p_name, const char *p_snapname,
IoCtx& c_ioctx, const char *c_name, ImageOptions& opts);
int clone4(IoCtx& p_ioctx, const char *p_name, uint64_t p_snap_id,
IoCtx& c_ioctx, const char *c_name, ImageOptions& opts);
int remove(IoCtx& io_ctx, const char *name);
int remove_with_progress(IoCtx& io_ctx, const char *name, ProgressContext& pctx);
int rename(IoCtx& src_io_ctx, const char *srcname, const char *destname);

View File

@ -716,28 +716,40 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
opts.set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
opts.set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
int r = clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx, nullptr,
c_name, opts, "", "");
int r = clone(p_ioctx, nullptr, p_name, CEPH_NOSNAP, p_snap_name,
c_ioctx, nullptr, c_name, opts, "", "");
opts.get(RBD_IMAGE_OPTION_ORDER, &order);
*c_order = order;
return r;
}
int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name,
const char *p_snap_name, IoCtx& c_ioctx, const char *c_id,
const char *c_name, ImageOptions& c_opts,
uint64_t p_snap_id, const char *p_snap_name, IoCtx& c_ioctx,
const char *c_id, const char *c_name, ImageOptions& c_opts,
const std::string &non_primary_global_image_id,
const std::string &primary_mirror_uuid)
{
CephContext *cct = (CephContext *)p_ioctx.cct();
ldout(cct, 10) << __func__
<< " p_id=" << (p_id ?: "")
<< ", p_name=" << (p_name ?: "")
<< ", p_snap_id=" << p_snap_id
<< ", p_snap_name=" << (p_snap_name ?: "")
<< ", c_id=" << (c_id ?: "")
<< ", c_name=" << c_name
<< ", c_opts=" << c_opts
<< ", non_primary_global_image_id=" << non_primary_global_image_id
<< ", primary_mirror_uuid=" << primary_mirror_uuid
<< dendl;
if (((p_id == nullptr) ^ (p_name == nullptr)) == 0) {
lderr(cct) << "must specify either parent image id or parent image name"
<< dendl;
return -EINVAL;
}
if (p_snap_name == nullptr) {
lderr(cct) << "image to be cloned must be a snapshot" << dendl;
if (((p_snap_id == CEPH_NOSNAP) ^ (p_snap_name == nullptr)) == 0) {
lderr(cct) << "must specify either parent snap id or parent snap name"
<< dendl;
return -EINVAL;
}
@ -770,10 +782,8 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
clone_id = c_id;
}
ldout(cct, 10) << __func__ << " "
<< "c_name=" << c_name << ", "
<< "c_id= " << clone_id << ", "
<< "c_opts=" << c_opts << dendl;
ldout(cct, 10) << __func__ << " parent_id=" << parent_id
<< ", clone_id=" << clone_id << dendl;
ConfigProxy config{reinterpret_cast<CephContext *>(c_ioctx.cct())->_conf};
api::Config<>::apply_pool_overrides(c_ioctx, &config);
@ -782,8 +792,8 @@ int validate_pool(IoCtx &io_ctx, CephContext *cct) {
C_SaferCond cond;
auto *req = image::CloneRequest<>::create(
config, p_ioctx, parent_id, p_snap_name,
{cls::rbd::UserSnapshotNamespace{}}, CEPH_NOSNAP, c_ioctx, c_name,
config, p_ioctx, parent_id, (p_snap_name ?: ""),
{cls::rbd::UserSnapshotNamespace{}}, p_snap_id, c_ioctx, c_name,
clone_id, c_opts, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL,
non_primary_global_image_id, primary_mirror_uuid,
asio_engine.get_work_queue(), &cond);

View File

@ -77,8 +77,8 @@ namespace librbd {
uint64_t features, int *c_order,
uint64_t stripe_unit, int stripe_count);
int clone(IoCtx& p_ioctx, const char *p_id, const char *p_name,
const char *p_snap_name, IoCtx& c_ioctx, const char *c_id,
const char *c_name, ImageOptions& c_opts,
uint64_t p_snap_id, const char *p_snap_name, IoCtx& c_ioctx,
const char *c_id, const char *c_name, ImageOptions& c_opts,
const std::string &non_primary_global_image_id,
const std::string &primary_mirror_uuid);
int rename(librados::IoCtx& io_ctx, const char *srcname, const char *dstname);

View File

@ -778,12 +778,26 @@ namespace librbd {
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioctx));
tracepoint(librbd, clone3_enter, p_ioctx.get_pool_name().c_str(), p_ioctx.get_id(), p_name, p_snap_name, c_ioctx.get_pool_name().c_str(), c_ioctx.get_id(), c_name, c_opts.opts);
int r = librbd::clone(p_ioctx, nullptr, p_name, p_snap_name, c_ioctx,
nullptr, c_name, c_opts, "", "");
int r = librbd::clone(p_ioctx, nullptr, p_name, CEPH_NOSNAP, p_snap_name,
c_ioctx, nullptr, c_name, c_opts, "", "");
tracepoint(librbd, clone3_exit, r);
return r;
}
int RBD::clone4(IoCtx& p_ioctx, const char *p_name, uint64_t p_snap_id,
IoCtx& c_ioctx, const char *c_name, ImageOptions& c_opts)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioctx));
tracepoint(librbd, clone4_enter, p_ioctx.get_pool_name().c_str(),
p_ioctx.get_id(), p_name, p_snap_id,
c_ioctx.get_pool_name().c_str(), c_ioctx.get_id(), c_name,
c_opts.opts);
int r = librbd::clone(p_ioctx, nullptr, p_name, p_snap_id, nullptr,
c_ioctx, nullptr, c_name, c_opts, "", "");
tracepoint(librbd, clone4_exit, r);
return r;
}
int RBD::remove(IoCtx& io_ctx, const char *name)
{
TracepointProvider::initialize<tracepoint_traits>(get_cct(io_ctx));
@ -3978,12 +3992,30 @@ extern "C" int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioc));
tracepoint(librbd, clone3_enter, p_ioc.get_pool_name().c_str(), p_ioc.get_id(), p_name, p_snap_name, c_ioc.get_pool_name().c_str(), c_ioc.get_id(), c_name, c_opts);
librbd::ImageOptions c_opts_(c_opts);
int r = librbd::clone(p_ioc, nullptr, p_name, p_snap_name, c_ioc, nullptr,
c_name, c_opts_, "", "");
int r = librbd::clone(p_ioc, nullptr, p_name, CEPH_NOSNAP, p_snap_name,
c_ioc, nullptr, c_name, c_opts_, "", "");
tracepoint(librbd, clone3_exit, r);
return r;
}
extern "C" int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
uint64_t p_snap_id, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts)
{
librados::IoCtx p_ioc, c_ioc;
librados::IoCtx::from_rados_ioctx_t(p_ioctx, p_ioc);
librados::IoCtx::from_rados_ioctx_t(c_ioctx, c_ioc);
TracepointProvider::initialize<tracepoint_traits>(get_cct(p_ioc));
tracepoint(librbd, clone4_enter, p_ioc.get_pool_name().c_str(),
p_ioc.get_id(), p_name, p_snap_id, c_ioc.get_pool_name().c_str(),
c_ioc.get_id(), c_name, c_opts);
librbd::ImageOptions c_opts_(c_opts);
int r = librbd::clone(p_ioc, nullptr, p_name, p_snap_id, nullptr,
c_ioc, nullptr, c_name, c_opts_, "", "");
tracepoint(librbd, clone4_exit, r);
return r;
}
extern "C" int rbd_remove(rados_ioctx_t p, const char *name)
{
librados::IoCtx io_ctx;

View File

@ -329,6 +329,9 @@ cdef extern from "rbd/librbd.h" nogil:
int rbd_clone3(rados_ioctx_t p_ioctx, const char *p_name,
const char *p_snapname, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts)
int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
uint64_t p_snap_id, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts)
int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
librbd_progress_fn_t cb, void *cbdata)
int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname,

View File

@ -350,6 +350,10 @@ cdef nogil:
const char *p_snapname, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts):
pass
int rbd_clone4(rados_ioctx_t p_ioctx, const char *p_name,
uint64_t p_snap_id, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts):
pass
int rbd_remove_with_progress(rados_ioctx_t io, const char *name,
librbd_progress_fn_t cb, void *cbdata):
pass

View File

@ -632,7 +632,7 @@ class RBD(object):
if ret < 0:
raise make_ex(ret, 'error creating image')
def clone(self, p_ioctx, p_name, p_snapname, c_ioctx, c_name,
def clone(self, p_ioctx, p_name, p_snapshot, c_ioctx, c_name,
features=None, order=None, stripe_unit=None, stripe_count=None,
data_pool=None, clone_format=None):
"""
@ -642,7 +642,7 @@ class RBD(object):
:type ioctx: :class:`rados.Ioctx`
:param p_name: the parent image name
:type name: str
:param p_snapname: the parent image snapshot name
:param p_snapshot: the parent image snapshot name or id
:type name: str
:param c_ioctx: the child context that represents the new clone
:type ioctx: :class:`rados.Ioctx`
@ -666,7 +666,6 @@ class RBD(object):
:raises: :class:`FunctionNotSupported`
:raises: :class:`ArgumentOutOfRange`
"""
p_snapname = cstr(p_snapname, 'p_snapname')
p_name = cstr(p_name, 'p_name')
c_name = cstr(c_name, 'c_name')
data_pool = cstr(data_pool, 'data_pool', opt=True)
@ -674,9 +673,18 @@ class RBD(object):
rados_ioctx_t _p_ioctx = convert_ioctx(p_ioctx)
rados_ioctx_t _c_ioctx = convert_ioctx(c_ioctx)
char *_p_name = p_name
char *_p_snapname = p_snapname
char *_p_snap_name
uint64_t _p_snap_id
char *_c_name = c_name
rbd_image_options_t opts
if isinstance(p_snapshot, str):
p_snap_name = cstr(p_snapshot, 'p_snapshot')
_p_snap_name = p_snap_name
elif isinstance(p_snapshot, int):
p_snap_name = None
_p_snap_id = p_snapshot
else:
raise TypeError("p_snapshot must be a string or an integer")
rbd_image_options_create(&opts)
try:
@ -698,9 +706,14 @@ class RBD(object):
if clone_format is not None:
rbd_image_options_set_uint64(opts, RBD_IMAGE_OPTION_CLONE_FORMAT,
clone_format)
with nogil:
ret = rbd_clone3(_p_ioctx, _p_name, _p_snapname,
_c_ioctx, _c_name, opts)
if p_snap_name is not None:
with nogil:
ret = rbd_clone3(_p_ioctx, _p_name, _p_snap_name,
_c_ioctx, _c_name, opts)
else:
with nogil:
ret = rbd_clone4(_p_ioctx, _p_name, _p_snap_id,
_c_ioctx, _c_name, opts)
finally:
rbd_image_options_destroy(opts)
if ret < 0:

View File

@ -15,6 +15,7 @@ from assertions import (assert_equal as eq, assert_raises, assert_not_equal,
assert_greater_equal)
from datetime import datetime, timedelta
from rados import (Rados,
LIBRADOS_SNAP_HEAD,
LIBRADOS_OP_FLAG_FADVISE_DONTNEED,
LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
LIBRADOS_OP_FLAG_FADVISE_RANDOM)
@ -33,6 +34,7 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
RBD_MIRROR_IMAGE_MODE_JOURNAL, RBD_MIRROR_IMAGE_MODE_SNAPSHOT,
RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
RBD_OPERATION_FEATURE_CLONE_CHILD,
RBD_SNAP_NAMESPACE_TYPE_GROUP,
RBD_SNAP_NAMESPACE_TYPE_TRASH,
RBD_SNAP_NAMESPACE_TYPE_MIRROR,
RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
@ -1800,6 +1802,67 @@ class TestClone(object):
# unprotect, remove parent snap happen in cleanup, and should succeed
def test_clone_by_snap_id(self):
clone_name2 = get_temp_image_name()
assert_raises(TypeError, self.rbd.clone, ioctx, image_name,
None, ioctx, clone_name2, features)
assert_raises(TypeError, self.rbd.clone, ioctx, image_name,
1.0, ioctx, clone_name2, features)
assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
LIBRADOS_SNAP_HEAD, ioctx, clone_name2, features)
self.image.create_snap('snap2')
snap_id = self.image.snap_get_id('snap2')
self.image.remove_snap('snap2')
assert_raises(ImageNotFound, self.image.snap_get_trash_namespace,
snap_id)
assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name,
snap_id, ioctx, clone_name2, features, clone_format=1)
assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name,
snap_id, ioctx, clone_name2, features, clone_format=2)
snap_id = self.image.snap_get_id('snap1')
self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2,
features, clone_format=1)
with Image(ioctx, clone_name2) as clone2:
assert clone2.parent_info() == self.clone.parent_info()
assert clone2.op_features() == 0
self.rbd.remove(ioctx, clone_name2)
self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2,
features, clone_format=2)
with Image(ioctx, clone_name2) as clone2:
assert clone2.parent_info() == self.clone.parent_info()
assert clone2.op_features() == RBD_OPERATION_FEATURE_CLONE_CHILD
self.rbd.remove(ioctx, clone_name2)
self.image.create_snap('snap2')
snap_id = self.image.snap_get_id('snap2')
assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
snap_id, ioctx, clone_name2, features, clone_format=1)
self.rbd.clone(ioctx, image_name, snap_id, ioctx, clone_name2,
features, clone_format=2)
with Image(ioctx, clone_name2) as clone2:
clone2_parent_info = clone2.parent_info()
clone_parent_info = self.clone.parent_info()
assert clone2_parent_info[0] == clone_parent_info[0]
assert clone2_parent_info[1] == clone_parent_info[1]
assert clone2_parent_info[2] == 'snap2'
assert clone_parent_info[2] == 'snap1'
self.image.remove_snap('snap2')
trash_snap = self.image.snap_get_trash_namespace(snap_id)
assert trash_snap == {
'original_name' : 'snap2'
}
clone_name3 = get_temp_image_name()
assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
snap_id, ioctx, clone_name3, features, clone_format=1)
assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name,
snap_id, ioctx, clone_name3, features, clone_format=2)
self.rbd.remove(ioctx, clone_name2)
assert_raises(ImageNotFound, self.image.snap_get_trash_namespace,
snap_id)
def test_stat(self):
image_info = self.image.stat()
clone_info = self.clone.stat()
@ -2820,7 +2883,7 @@ class TestGroups(object):
eq([snap_name], [snap['name'] for snap in self.group.list_snaps()])
for snap in self.image.list_snaps():
eq(rbd.RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace'])
eq(RBD_SNAP_NAMESPACE_TYPE_GROUP, snap['namespace'])
info = snap['group']
eq(group_name, info['group_name'])
eq(snap_name, info['group_snap_name'])
@ -2885,6 +2948,107 @@ class TestGroups(object):
self.group.remove_snap(new_snap_name)
eq([], list(self.group.list_snaps()))
@require_features([RBD_FEATURE_LAYERING])
def test_group_snap_clone(self):
data = rand_data(256)
with Image(ioctx, image_name) as image:
image.write(data, 0)
self.group.add_image(ioctx, image_name)
self.group.create_snap(snap_name)
assert [s['name'] for s in self.group.list_snaps()] == [snap_name]
image_snaps = list(self.image.list_snaps())
assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_GROUP]
image_snap_name = image_snaps[0]['name']
image_snap_id = image_snaps[0]['id']
assert image_snaps[0]['group'] == {
'pool' : ioctx.get_pool_id(),
'name' : group_name,
'snap_name' : snap_name,
}
clone_name = get_temp_image_name()
assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name,
image_snap_name, ioctx, clone_name, features, clone_format=1)
assert_raises(InvalidArgument, self.rbd.clone, ioctx, image_name,
image_snap_id, ioctx, clone_name, features, clone_format=1)
assert_raises(ImageNotFound, self.rbd.clone, ioctx, image_name,
image_snap_name, ioctx, clone_name, features, clone_format=2)
self.rbd.clone(ioctx, image_name, image_snap_id, ioctx, clone_name,
features, clone_format=2)
with Image(ioctx, clone_name) as clone:
parent_spec = clone.get_parent_image_spec()
assert parent_spec['pool_name'] == pool_name
assert parent_spec['image_name'] == image_name
assert parent_spec['snap_namespace_type'] == RBD_SNAP_NAMESPACE_TYPE_GROUP
assert parent_spec['snap_name'] == image_snap_name
assert parent_spec['snap_id'] == image_snap_id
read = clone.read(0, 256)
assert read == data
self.group.remove_snap(snap_name)
assert list(self.group.list_snaps()) == []
image_snaps = list(self.image.list_snaps())
assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_TRASH]
trash_image_snap_name = image_snaps[0]['name']
assert image_snaps[0]['id'] == image_snap_id
assert image_snaps[0]['trash'] == {
'original_name' : image_snap_name
}
assert trash_image_snap_name != image_snap_name
with Image(ioctx, clone_name) as clone:
parent_spec = clone.get_parent_image_spec()
assert parent_spec['pool_name'] == pool_name
assert parent_spec['image_name'] == image_name
assert parent_spec['snap_namespace_type'] == RBD_SNAP_NAMESPACE_TYPE_TRASH
assert parent_spec['snap_name'] == trash_image_snap_name
assert parent_spec['snap_id'] == image_snap_id
read = clone.read(0, 256)
assert read == data
self.rbd.remove(ioctx, clone_name)
assert list(self.image.list_snaps()) == []
@require_features([RBD_FEATURE_LAYERING])
def test_group_snap_clone_flatten(self):
data = rand_data(256)
with Image(ioctx, image_name) as image:
image.write(data, 0)
self.group.add_image(ioctx, image_name)
self.group.create_snap(snap_name)
assert [s['name'] for s in self.group.list_snaps()] == [snap_name]
image_snaps = list(self.image.list_snaps())
assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_GROUP]
image_snap_id = image_snaps[0]['id']
clone_name = get_temp_image_name()
self.rbd.clone(ioctx, image_name, image_snap_id, ioctx, clone_name,
features, clone_format=2)
self.group.remove_snap(snap_name)
assert list(self.group.list_snaps()) == []
image_snaps = list(self.image.list_snaps())
assert [s['namespace'] for s in image_snaps] == [RBD_SNAP_NAMESPACE_TYPE_TRASH]
assert image_snaps[0]['id'] == image_snap_id
with Image(ioctx, clone_name) as clone:
parent_spec = clone.get_parent_image_spec()
assert parent_spec['pool_id'] == ioctx.get_pool_id()
assert parent_spec['image_id'] == self.image.id()
assert parent_spec['snap_id'] == image_snap_id
read = clone.read(0, 256)
assert read == data
clone.flatten()
assert list(self.image.list_snaps()) == []
with Image(ioctx, clone_name) as clone:
assert_raises(ImageNotFound, clone.get_parent_image_spec)
read = clone.read(0, 256)
assert read == data
self.rbd.remove(ioctx, clone_name)
def test_group_snap_rollback(self):
eq([], list(self.group.list_images()))
self.group.add_image(ioctx, image_name)

View File

@ -202,7 +202,7 @@ public:
librbd::ImageOptions clone_opts;
clone_opts.set(RBD_IMAGE_OPTION_FEATURES, ictx->features);
EXPECT_EQ(0, librbd::clone(m_local_io_ctx, m_local_image_id.c_str(),
nullptr, "snap1", m_local_io_ctx,
nullptr, CEPH_NOSNAP, "snap1", m_local_io_ctx,
clone_id.c_str(), "clone1", clone_opts,
GLOBAL_CLONE_IMAGE_ID, m_remote_mirror_uuid));

View File

@ -1481,6 +1481,36 @@ TRACEPOINT_EVENT(librbd, clone3_exit,
)
)
TRACEPOINT_EVENT(librbd, clone4_enter,
TP_ARGS(
const char*, parent_pool_name,
uint64_t, parent_pool_id,
const char*, parent_name,
uint64_t, parent_snap_id,
const char*, child_pool_name,
uint64_t, child_pool_id,
const char*, child_name,
void*, opts),
TP_FIELDS(
ctf_string(parent_pool_name, parent_pool_name)
ctf_integer(uint64_t, parent_pool_id, parent_pool_id)
ctf_string(parent_name, parent_name)
ctf_integer(uint64_t, parent_snap_id, parent_snap_id)
ctf_string(child_pool_name, child_pool_name)
ctf_integer(uint64_t, child_pool_id, child_pool_id)
ctf_string(child_name, child_name)
ctf_integer_hex(void*, opts, opts)
)
)
TRACEPOINT_EVENT(librbd, clone4_exit,
TP_ARGS(
int, retval),
TP_FIELDS(
ctf_integer(int, retval, retval)
)
)
TRACEPOINT_EVENT(librbd, flatten_enter,
TP_ARGS(
void*, imagectx,