bdev: Add support for compare operations
Signed-off-by: Maciej Szwed <maciej.szwed@intel.com> Change-Id: I45b99abe1ed760aa59785e5c4b4f71917e1f92c3 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/477458 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
parent
c7092e2b3a
commit
f8d024b8c1
@ -137,6 +137,7 @@ enum spdk_bdev_io_type {
|
||||
SPDK_BDEV_IO_TYPE_GET_ZONE_INFO,
|
||||
SPDK_BDEV_IO_TYPE_ZONE_MANAGEMENT,
|
||||
SPDK_BDEV_IO_TYPE_ZONE_APPEND,
|
||||
SPDK_BDEV_IO_TYPE_COMPARE,
|
||||
SPDK_BDEV_NUM_IO_TYPES /* Keep last */
|
||||
};
|
||||
|
||||
@ -1013,6 +1014,117 @@ int spdk_bdev_writev_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a compare request to the bdev on the given channel.
|
||||
*
|
||||
* \ingroup bdev_io_submit_functions
|
||||
*
|
||||
* \param desc Block device descriptor.
|
||||
* \param ch I/O channel. Obtained by calling spdk_bdev_get_io_channel().
|
||||
* \param buf Data buffer to compare to.
|
||||
* \param offset_blocks The offset, in blocks, from the start of the block device.
|
||||
* \param num_blocks The number of blocks to compare. buf must be greater than or equal to this size.
|
||||
* \param cb Called when the request is complete.
|
||||
* \param cb_arg Argument passed to cb.
|
||||
*
|
||||
* \return 0 on success. On success, the callback will always
|
||||
* be called (even if the request ultimately failed). Return
|
||||
* negated errno on failure, in which case the callback will not be called.
|
||||
* * -EINVAL - offset_blocks and/or num_blocks are out of range
|
||||
* * -ENOMEM - spdk_bdev_io buffer cannot be allocated
|
||||
*/
|
||||
int spdk_bdev_compare_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
void *buf, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a compare request to the bdev on the given channel. This function uses
|
||||
* separate buffer for metadata transfer (valid only if bdev supports this
|
||||
* mode).
|
||||
*
|
||||
* \ingroup bdev_io_submit_functions
|
||||
*
|
||||
* \param desc Block device descriptor.
|
||||
* \param ch I/O channel. Obtained by calling spdk_bdev_get_io_channel().
|
||||
* \param buf Data buffer to compare to.
|
||||
* \param md Metadata buffer.
|
||||
* \param offset_blocks The offset, in blocks, from the start of the block device.
|
||||
* \param num_blocks The number of blocks to compare. buf must be greater than or equal to this size.
|
||||
* \param cb Called when the request is complete.
|
||||
* \param cb_arg Argument passed to cb.
|
||||
*
|
||||
* \return 0 on success. On success, the callback will always
|
||||
* be called (even if the request ultimately failed). Return
|
||||
* negated errno on failure, in which case the callback will not be called.
|
||||
* * -EINVAL - offset_blocks and/or num_blocks are out of range or separate
|
||||
* metadata is not supported
|
||||
* * -ENOMEM - spdk_bdev_io buffer cannot be allocated
|
||||
*/
|
||||
int spdk_bdev_compare_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
void *buf, void *md, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a compare request to the bdev on the given channel. This differs from
|
||||
* spdk_bdev_compare by allowing the data buffer to be described in a scatter
|
||||
* gather list. Some physical devices place memory alignment requirements on
|
||||
* data and may not be able to directly transfer out of the buffers provided. In
|
||||
* this case, the request may fail.
|
||||
*
|
||||
* \ingroup bdev_io_submit_functions
|
||||
*
|
||||
* \param desc Block device descriptor.
|
||||
* \param ch I/O channel. Obtained by calling spdk_bdev_get_io_channel().
|
||||
* \param iov A scatter gather list of buffers to be compared to.
|
||||
* \param iovcnt The number of elements in iov.
|
||||
* \param offset_blocks The offset, in blocks, from the start of the block device.
|
||||
* \param num_blocks The number of blocks to compare.
|
||||
* \param cb Called when the request is complete.
|
||||
* \param cb_arg Argument passed to cb.
|
||||
*
|
||||
* \return 0 on success. On success, the callback will always
|
||||
* be called (even if the request ultimately failed). Return
|
||||
* negated errno on failure, in which case the callback will not be called.
|
||||
* * -EINVAL - offset_blocks and/or num_blocks are out of range
|
||||
* * -ENOMEM - spdk_bdev_io buffer cannot be allocated
|
||||
*/
|
||||
int spdk_bdev_comparev_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
struct iovec *iov, int iovcnt,
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a compare request to the bdev on the given channel. This differs from
|
||||
* spdk_bdev_compare by allowing the data buffer to be described in a scatter
|
||||
* gather list. Some physical devices place memory alignment requirements on
|
||||
* data or metadata and may not be able to directly transfer out of the buffers
|
||||
* provided. In this case, the request may fail. This function uses separate
|
||||
* buffer for metadata transfer (valid only if bdev supports this mode).
|
||||
*
|
||||
* \ingroup bdev_io_submit_functions
|
||||
*
|
||||
* \param desc Block device descriptor.
|
||||
* \param ch I/O channel. Obtained by calling spdk_bdev_get_io_channel().
|
||||
* \param iov A scatter gather list of buffers to be compared to.
|
||||
* \param iovcnt The number of elements in iov.
|
||||
* \param md Metadata buffer.
|
||||
* \param offset_blocks The offset, in blocks, from the start of the block device.
|
||||
* \param num_blocks The number of blocks to compare.
|
||||
* \param cb Called when the request is complete.
|
||||
* \param cb_arg Argument passed to cb.
|
||||
*
|
||||
* \return 0 on success. On success, the callback will always
|
||||
* be called (even if the request ultimately failed). Return
|
||||
* negated errno on failure, in which case the callback will not be called.
|
||||
* * -EINVAL - offset_blocks and/or num_blocks are out of range or separate
|
||||
* metadata is not supported
|
||||
* * -ENOMEM - spdk_bdev_io buffer cannot be allocated
|
||||
*/
|
||||
int spdk_bdev_comparev_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
struct iovec *iov, int iovcnt, void *md,
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a request to acquire a data buffer that represents the given
|
||||
* range of blocks. The data buffer is placed in the spdk_bdev_io structure
|
||||
|
@ -222,6 +222,7 @@ struct spdk_bdev_fn_table {
|
||||
|
||||
/** bdev I/O completion status */
|
||||
enum spdk_bdev_io_status {
|
||||
SPDK_BDEV_IO_STATUS_MISCOMPARE = -5,
|
||||
/*
|
||||
* NOMEM should be returned when a bdev module cannot start an I/O because of
|
||||
* some lack of resources. It may not be returned for RESET I/O. I/O completed
|
||||
|
191
lib/bdev/bdev.c
191
lib/bdev/bdev.c
@ -3263,6 +3263,197 @@ spdk_bdev_writev_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_chan
|
||||
num_blocks, cb, cb_arg);
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_compare_do_read_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
||||
{
|
||||
struct spdk_bdev_io *parent_io = cb_arg;
|
||||
uint8_t *read_buf = bdev_io->u.bdev.iovs[0].iov_base;
|
||||
int i, rc = 0;
|
||||
|
||||
if (!success) {
|
||||
parent_io->internal.status = SPDK_BDEV_IO_STATUS_FAILED;
|
||||
parent_io->internal.cb(parent_io, false, parent_io->internal.caller_ctx);
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < parent_io->u.bdev.iovcnt; i++) {
|
||||
rc = memcmp(read_buf,
|
||||
parent_io->u.bdev.iovs[i].iov_base,
|
||||
parent_io->u.bdev.iovs[i].iov_len);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
read_buf += parent_io->u.bdev.iovs[i].iov_len;
|
||||
}
|
||||
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
|
||||
if (rc == 0) {
|
||||
parent_io->internal.status = SPDK_BDEV_IO_STATUS_SUCCESS;
|
||||
parent_io->internal.cb(parent_io, true, parent_io->internal.caller_ctx);
|
||||
} else {
|
||||
parent_io->internal.status = SPDK_BDEV_IO_STATUS_MISCOMPARE;
|
||||
parent_io->internal.cb(parent_io, false, parent_io->internal.caller_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_compare_do_read(void *_bdev_io)
|
||||
{
|
||||
struct spdk_bdev_io *bdev_io = _bdev_io;
|
||||
int rc;
|
||||
|
||||
rc = spdk_bdev_read_blocks(bdev_io->internal.desc,
|
||||
spdk_io_channel_from_ctx(bdev_io->internal.ch), NULL,
|
||||
bdev_io->u.bdev.offset_blocks, bdev_io->u.bdev.num_blocks,
|
||||
bdev_compare_do_read_done, bdev_io);
|
||||
|
||||
if (rc == -ENOMEM) {
|
||||
bdev_queue_io_wait_with_cb(bdev_io, bdev_compare_do_read);
|
||||
} else if (rc != 0) {
|
||||
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_FAILED;
|
||||
bdev_io->internal.cb(bdev_io, false, bdev_io->internal.caller_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
bdev_comparev_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
struct iovec *iov, int iovcnt, void *md_buf,
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(desc);
|
||||
struct spdk_bdev_io *bdev_io;
|
||||
struct spdk_bdev_channel *channel = spdk_io_channel_get_ctx(ch);
|
||||
|
||||
if (!bdev_io_valid_blocks(bdev, offset_blocks, num_blocks)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bdev_io = bdev_channel_get_io(channel);
|
||||
if (!bdev_io) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bdev_io->internal.ch = channel;
|
||||
bdev_io->internal.desc = desc;
|
||||
bdev_io->type = SPDK_BDEV_IO_TYPE_COMPARE;
|
||||
bdev_io->u.bdev.iovs = iov;
|
||||
bdev_io->u.bdev.iovcnt = iovcnt;
|
||||
bdev_io->u.bdev.md_buf = md_buf;
|
||||
bdev_io->u.bdev.num_blocks = num_blocks;
|
||||
bdev_io->u.bdev.offset_blocks = offset_blocks;
|
||||
bdev_io_init(bdev_io, bdev, cb_arg, cb);
|
||||
|
||||
if (bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_COMPARE)) {
|
||||
bdev_io_submit(bdev_io);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bdev_compare_do_read(bdev_io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_comparev_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
struct iovec *iov, int iovcnt,
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
return bdev_comparev_blocks_with_md(desc, ch, iov, iovcnt, NULL, offset_blocks,
|
||||
num_blocks, cb, cb_arg);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_comparev_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
struct iovec *iov, int iovcnt, void *md_buf,
|
||||
uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
if (!spdk_bdev_is_md_separate(spdk_bdev_desc_get_bdev(desc))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!_bdev_io_check_md_buf(iov, md_buf)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bdev_comparev_blocks_with_md(desc, ch, iov, iovcnt, md_buf, offset_blocks,
|
||||
num_blocks, cb, cb_arg);
|
||||
}
|
||||
|
||||
static int
|
||||
bdev_compare_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
void *buf, void *md_buf, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(desc);
|
||||
struct spdk_bdev_io *bdev_io;
|
||||
struct spdk_bdev_channel *channel = spdk_io_channel_get_ctx(ch);
|
||||
|
||||
if (!bdev_io_valid_blocks(bdev, offset_blocks, num_blocks)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bdev_io = bdev_channel_get_io(channel);
|
||||
if (!bdev_io) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
bdev_io->internal.ch = channel;
|
||||
bdev_io->internal.desc = desc;
|
||||
bdev_io->type = SPDK_BDEV_IO_TYPE_COMPARE;
|
||||
bdev_io->u.bdev.iovs = &bdev_io->iov;
|
||||
bdev_io->u.bdev.iovs[0].iov_base = buf;
|
||||
bdev_io->u.bdev.iovs[0].iov_len = num_blocks * bdev->blocklen;
|
||||
bdev_io->u.bdev.iovcnt = 1;
|
||||
bdev_io->u.bdev.md_buf = md_buf;
|
||||
bdev_io->u.bdev.num_blocks = num_blocks;
|
||||
bdev_io->u.bdev.offset_blocks = offset_blocks;
|
||||
bdev_io_init(bdev_io, bdev, cb_arg, cb);
|
||||
|
||||
if (bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_COMPARE)) {
|
||||
bdev_io_submit(bdev_io);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bdev_compare_do_read(bdev_io);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_compare_blocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
void *buf, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
return bdev_compare_blocks_with_md(desc, ch, buf, NULL, offset_blocks, num_blocks,
|
||||
cb, cb_arg);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_compare_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
|
||||
void *buf, void *md_buf, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
spdk_bdev_io_completion_cb cb, void *cb_arg)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
};
|
||||
|
||||
if (!spdk_bdev_is_md_separate(spdk_bdev_desc_get_bdev(desc))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!_bdev_io_check_md_buf(&iov, md_buf)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return bdev_compare_blocks_with_md(desc, ch, buf, md_buf, offset_blocks, num_blocks,
|
||||
cb, cb_arg);
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_zcopy_get_buf(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io, bool success)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user