* FALLOCATE: - 5
* PUNCH HOLE: - 6
* WRITESAME: - 7
+ * COMPAREANDWRITE: - 8
*
* When mapped read/writes are disabled, they are simply converted to normal
* reads and writes. When fallocate/fpunch calls are disabled, they are
#define OP_FALLOCATE 5
#define OP_PUNCH_HOLE 6
#define OP_WRITESAME 7
+#define OP_COMPARE_AND_WRITE 8
/* rbd-specific operations */
-#define OP_CLONE 8
-#define OP_FLATTEN 9
-#define OP_MAX_FULL 10
+#define OP_CLONE 9
+#define OP_FLATTEN 10
+#define OP_MAX_FULL 11
/* operation modifiers */
#define OP_CLOSEOPEN 100
int (*flatten)(struct rbd_ctx *ctx);
ssize_t (*writesame)(struct rbd_ctx *ctx, uint64_t off, size_t len,
const char *buf, size_t data_len);
+ ssize_t (*compare_and_write)(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *cmp_buf, const char *buf);
};
char *pool; /* name of the pool our test image is in */
return n;
}
+ssize_t
+librbd_compare_and_write(struct rbd_ctx *ctx, uint64_t off, size_t len,
+ const char *cmp_buf, const char *buf)
+{
+ ssize_t n;
+ int ret;
+ uint64_t mismatch_off = 0;
+
+ n = rbd_compare_and_write(ctx->image, off, len, cmp_buf, buf, &mismatch_off, 0);
+ if (n == -EINVAL) {
+ return n;
+ } else if (n < 0) {
+ prt("rbd_compare_and_write mismatch(%llu, %zu, %llu) failed\n",
+ off, len, mismatch_off);
+ return n;
+ }
+
+ ret = librbd_verify_object_map(ctx);
+ if (ret < 0) {
+ return ret;
+ }
+ return n;
+
+}
+
int
librbd_get_size(struct rbd_ctx *ctx, uint64_t *size)
{
librbd_clone,
librbd_flatten,
librbd_writesame,
+ librbd_compare_and_write,
};
int
int ret;
/*
- * BLKDISCARD goes straight to disk and doesn't do anything
+ * BLKZEROOUT goes straight to disk and doesn't do anything
* about dirty buffers. This means we need to flush so that
*
* write 0..3M
*
* returns "data 0000 data" rather than "data data data" in
* case 1..2M was cached.
+ *
+ * Note: These cache coherency issues are supposed to be fixed
+ * in recent kernels.
*/
ret = __krbd_flush(ctx, true);
if (ret < 0)
return ret;
/*
- * off and len must be 512-byte aligned, otherwise BLKDISCARD
+ * off and len must be 512-byte aligned, otherwise BLKZEROOUT
* will fail with -EINVAL. This means that -K (enable krbd
* mode) requires -h 512 or similar.
*/
- if (ioctl(ctx->krbd_fd, BLKDISCARD, &range) < 0) {
+ if (ioctl(ctx->krbd_fd, BLKZEROOUT, &range) < 0) {
ret = -errno;
- prt("BLKDISCARD(%llu, %llu) failed\n", off, len);
+ prt("BLKZEROOUT(%llu, %llu) failed\n", off, len);
return ret;
}
badoff < lp->args[0] + lp->args[1])
prt("\t***WSWSWSWS");
break;
+ case OP_COMPARE_AND_WRITE:
+ prt("COMPARE_AND_WRITE 0x%x thru 0x%x\t(0x%x bytes)",
+ lp->args[0], lp->args[0] + lp->args[1] - 1,
+ lp->args[1]);
+ if (lp->args[0] > lp->args[2])
+ prt(" HOLE");
+ else if (lp->args[0] + lp->args[1] > lp->args[2])
+ prt(" EXTEND");
+ if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
+ badoff < lp->args[0] + lp->args[1])
+ prt("\t***WWWW");
+ break;
case OP_CLONE:
prt("CLONE");
break;
simple_err("Error creating ioctx", r);
goto failed_krbd;
}
+ rados_application_enable(ioctx, "rbd", 1);
+
if (clone_calls || journal_replay) {
uint64_t features = 0;
if (clone_calls) {
doflush(offset, size);
}
+void
+docompareandwrite(unsigned offset, unsigned size)
+{
+ int ret;
+
+ if (skip_partial_discard) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("compare and write disabled\n");
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ return;
+ }
+
+ offset -= offset % writebdy;
+ if (o_direct)
+ size -= size % writebdy;
+
+ if (size == 0) {
+ if (!quiet && testcalls > simulatedopcount && !o_direct)
+ prt("skipping zero size read\n");
+ log4(OP_SKIPPED, OP_READ, offset, size);
+ return;
+ }
+
+ if (size + offset > file_size) {
+ if (!quiet && testcalls > simulatedopcount)
+ prt("skipping seek/compare past end of file\n");
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ return;
+ }
+
+ memcpy(temp_buf + offset, good_buf + offset, size);
+ gendata(original_buf, good_buf, offset, size);
+ log4(OP_COMPARE_AND_WRITE, offset, size, 0);
+
+ if (testcalls <= simulatedopcount)
+ return;
+
+ if (!quiet &&
+ ((progressinterval && testcalls % progressinterval == 0) ||
+ (debug &&
+ (monitorstart == -1 ||
+ (static_cast<long>(offset + size) > monitorstart &&
+ (monitorend == -1 ||
+ static_cast<long>(offset) <= monitorend))))))
+ prt("%lu compareandwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
+ offset, offset + size - 1, size);
+
+ ret = ops->compare_and_write(&ctx, offset, size, temp_buf + offset,
+ good_buf + offset);
+ if (ret != (ssize_t)size) {
+ if (ret == -EINVAL) {
+ memcpy(good_buf + offset, temp_buf + offset, size);
+ return;
+ }
+ if (ret < 0)
+ prterrcode("docompareandwrite: ops->compare_and_write", ret);
+ else
+ prt("short write: 0x%x bytes instead of 0x%x\n", ret, size);
+ report_failure(151);
+ return;
+ }
+
+ if (flush_enabled)
+ doflush(offset, size);
+}
+
void clone_filename(char *buf, size_t len, int clones)
{
snprintf(buf, len, "%s/fsx-%s-parent%d",
log4(OP_SKIPPED, OP_WRITESAME, offset, size);
goto out;
}
+ break;
+ case OP_COMPARE_AND_WRITE:
+ /* compare_and_write not implemented */
+ if (!ops->compare_and_write) {
+ log4(OP_SKIPPED, OP_COMPARE_AND_WRITE, offset, size);
+ goto out;
+ }
+ break;
}
switch (op) {
TRIM_OFF_LEN(offset, size, maxfilelen);
dowritesame(offset, size);
break;
+ case OP_COMPARE_AND_WRITE:
+ TRIM_OFF_LEN(offset, size, file_size);
+ docompareandwrite(offset, size);
+ break;
case OP_CLONE:
do_clone();