--- /dev/null
+nvme-compare(1)
+===============
+
+NAME
+----
+nvme-compare - Send an NVMe Compare command, provide results
+
+SYNOPSIS
+--------
+[verse]
+'nvme-compare' <device> [--start-block=<slba> | -s <slba>]
+ [--block-count=<nlb> | -c <nlb>]
+ [--data-size=<size> | -z <size>]
+ [--ref-tag=<reftag> | -r <reftag>]
+ [--data=<data-file> | -d <data-file>]
+ [--prinfo=<prinfo> | -p <prinfo>]
+ [--app-tag-mask=<appmask> | -m <appmask>]
+ [--app-tag=<apptag> | -a <apptag>]
+ [--limited-retry | -l]
+ [--force-unit-access | -f]
+
+DESCRIPTION
+-----------
+The Compare command reads the logical blocks specified by the command
+from the medium and compares the data read to a comparison data buffer
+transferred as part of the command. If the data read from the controller
+and the comparison data buffer are equivalent with no miscompares,
+then the command completes successfully. If there is any miscompare,
+the command completes with an error of Compare Failure. If metadata is
+provided, then a comparison is also performed for the metadata.
+
+OPTIONS
+-------
+--start-block=<slba>::
+-s <slba>::
+ Start block.
+
+EXAMPLES
+--------
+No examples yet.
+
+NVME
+----
+Part of the nvme-user suite
ENTRY(RESV_RELEASE, "resv-release", "Submit a Reservation Release, return results", resv_release) \
ENTRY(RESV_REPORT, "resv-report", "Submit a Reservation Report, return results", resv_report) \
ENTRY(FLUSH, "flush", "Submit a Flush command, return results", flush) \
+ ENTRY(COMPARE, "compare", "Submit a Comapre command, return results", compare) \
ENTRY(HELP, "help", "Display this help", help)
#define ENTRY(i, n, h, f) \
return 0;
}
+static int compare(int argc, char **argv)
+{
+ struct nvme_user_io io;
+ void *buffer;
+ int err, opt, dfd = STDIN_FILENO, long_index = 0;
+ unsigned int data_size = 0;
+ __u8 prinfo = 0;
+ static struct option opts[] = {
+ {"start-block", required_argument, 0, 's'},
+ {"block-count", required_argument, 0, 'c'},
+ {"data-size", required_argument, 0, 'z'},
+ {"ref-tag", required_argument, 0, 'r'},
+ {"data", required_argument, 0, 'd'},
+ {"prinfo", required_argument, 0, 'p'},
+ {"app-tag-mask", required_argument, 0, 'm'},
+ {"app-tag", required_argument, 0, 'a'},
+ {"limited-retry", no_argument, 0, 'l'},
+ {"force-unit-access", no_argument, 0, 'f'},
+ { 0, 0, 0, 0}
+ };
+
+ memset(&io, 0, sizeof(io));
+ while ((opt = getopt_long(argc, (char **)argv, "p:s:c:z:r:d:m:a:lf", opts,
+ &long_index)) != -1) {
+ switch(opt) {
+ case 's': get_long(optarg, &io.slba); break;
+ case 'c': get_short(optarg, &io.nblocks); break;
+ case 'z': get_int(optarg, &data_size); break;
+ case 'r': get_int(optarg, &io.reftag); break;
+ case 'm': get_short(optarg, &io.appmask); break;
+ case 'a': get_short(optarg, &io.apptag); break;
+ case 'p':
+ get_byte(optarg, &prinfo);
+ if (prinfo > 0xf)
+ return EINVAL;
+ io.control |= (prinfo << 10);
+ break;
+ case 'l':
+ io.control |= NVME_RW_LR;
+ break;
+ case 'f':
+ io.control | NVME_RW_FUA;
+ break;
+ case 'd':
+ dfd = open(optarg, O_RDONLY);
+ if (dfd < 0) {
+ perror(optarg);
+ return EINVAL;
+ }
+ break;
+ default:
+ return EINVAL;
+ }
+ };
+ get_dev(optind, argc, argv);
+
+ if (!data_size) {
+ fprintf(stderr, "data size not provided\n");
+ return EINVAL;
+ }
+ buffer = malloc(data_size);
+ if (read(dfd, (void *)buffer, data_size) < 0) {
+ fprintf(stderr, "failed to read compare buffer\n");
+ return EINVAL;
+ }
+
+ io.opcode = nvme_cmd_compare;
+ io.addr = (__u64)buffer;
+ err = ioctl(fd, NVME_IOCTL_SUBMIT_IO, &io);
+ if (err < 0)
+ perror("ioctl");
+ else
+ printf("compare:%s(%04x)\n", nvme_status_to_string(err), err);
+ return 0;
+}
+
static int sec_recv(int argc, char **argv)
{
struct nvme_admin_cmd cmd;